@json-to-office/core-docx 0.27.0 → 0.29.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/index.js CHANGED
@@ -1924,6 +1924,7 @@ var init_docxImagePositioning = __esm({
1924
1924
 
1925
1925
  // src/core/generator.ts
1926
1926
  import { writeFileSync } from "fs";
1927
+ import { dirname, resolve as resolve3 } from "path";
1927
1928
 
1928
1929
  // src/types/index.ts
1929
1930
  import {
@@ -2698,9 +2699,34 @@ import {
2698
2699
  } from "docx";
2699
2700
 
2700
2701
  // src/utils/imageUtils.ts
2701
- init_widthUtils();
2702
2702
  import { readFileSync } from "fs";
2703
2703
  import probe from "probe-image-size";
2704
+
2705
+ // src/utils/generationContext.ts
2706
+ import { AsyncLocalStorage } from "async_hooks";
2707
+ import { isAbsolute, resolve } from "path";
2708
+ var generationDateStorage = new AsyncLocalStorage();
2709
+ function runWithGenerationDate(date, callback) {
2710
+ return generationDateStorage.run(date, callback);
2711
+ }
2712
+ function getGenerationDate() {
2713
+ return generationDateStorage.getStore() ?? /* @__PURE__ */ new Date();
2714
+ }
2715
+ var baseDirStorage = new AsyncLocalStorage();
2716
+ function runWithBaseDir(baseDir, callback) {
2717
+ return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
2718
+ }
2719
+ function getBaseDir() {
2720
+ return baseDirStorage.getStore();
2721
+ }
2722
+ function resolveFromBaseDir(filePath) {
2723
+ const base = baseDirStorage.getStore();
2724
+ if (!base || isAbsolute(filePath)) return filePath;
2725
+ return resolve(base, filePath);
2726
+ }
2727
+
2728
+ // src/utils/imageUtils.ts
2729
+ init_widthUtils();
2704
2730
  import { ImageRun } from "docx";
2705
2731
  function parseWidthValue(width, availableWidthPx) {
2706
2732
  if (typeof width === "number") {
@@ -2876,7 +2902,7 @@ async function getImageBuffer(imagePath) {
2876
2902
  if (isValidUrl(imagePath)) {
2877
2903
  return await downloadImageFromUrl(imagePath);
2878
2904
  }
2879
- return { buffer: readFileSync(imagePath) };
2905
+ return { buffer: readFileSync(resolveFromBaseDir(imagePath)) };
2880
2906
  }
2881
2907
  async function getImageDimensions(imagePath) {
2882
2908
  try {
@@ -3948,16 +3974,6 @@ function parseTextWithHyperlinks(text, baseStyle = {}, options = {}) {
3948
3974
  return runs;
3949
3975
  }
3950
3976
 
3951
- // src/utils/generationContext.ts
3952
- import { AsyncLocalStorage } from "async_hooks";
3953
- var generationDateStorage = new AsyncLocalStorage();
3954
- function runWithGenerationDate(date, callback) {
3955
- return generationDateStorage.run(date, callback);
3956
- }
3957
- function getGenerationDate() {
3958
- return generationDateStorage.getStore() ?? /* @__PURE__ */ new Date();
3959
- }
3960
-
3961
3977
  // src/utils/placeholderProcessor.ts
3962
3978
  function styledPlaceholderText(text, context) {
3963
3979
  return new TextRun2({
@@ -4312,8 +4328,9 @@ async function renderComponentWithCache(component, theme, themeName, context, by
4312
4328
  const themeHash = createThemeHash(theme);
4313
4329
  const contextKey = context?.section ? `${context.section.currentLayout}:${context.section.columnCount}` : "no-section";
4314
4330
  const generationDateKey = getGenerationDate().toISOString();
4331
+ const baseDirKey = getBaseDir() ?? "";
4315
4332
  const childrenKey = "children" in component && component.children ? `:children:${JSON.stringify(component.children)}` : "";
4316
- const cacheKey = `component:${component.name}:${themeHash}:${contextKey}:${generationDateKey}:${componentProps}${childrenKey}`;
4333
+ const cacheKey = `component:${component.name}:${themeHash}:${contextKey}:${generationDateKey}:${baseDirKey}:${componentProps}${childrenKey}`;
4317
4334
  const cached = await componentCache.get(cacheKey);
4318
4335
  if (cached) {
4319
4336
  return cached.result;
@@ -6996,6 +7013,7 @@ async function renderHighchartsComponent(component, theme, themeName, context) {
6996
7013
  }
6997
7014
 
6998
7015
  // src/components/visual.ts
7016
+ import { createHash as createHash4 } from "crypto";
6999
7017
  import {
7000
7018
  clampVisualDpi,
7001
7019
  DEFAULT_VISUAL_DPI
@@ -7045,14 +7063,21 @@ function visualToImageProps(props, base64DataUri) {
7045
7063
  ...props.alt !== void 0 && { alt: props.alt }
7046
7064
  };
7047
7065
  }
7048
- async function rasterize(presentation, dpi, propsServerUrl, serviceConfig) {
7066
+ function visualRasterKey(presentation, dpi, serverUrl) {
7067
+ return createHash4("sha256").update(JSON.stringify({ p: presentation, d: dpi, u: serverUrl ?? null })).digest("hex");
7068
+ }
7069
+ function effectiveVisualServerUrl(props, serviceConfig) {
7070
+ if (serviceConfig?.render) return void 0;
7071
+ return props.serverUrl;
7072
+ }
7073
+ async function rasterizeVisualSlide(presentation, dpi, propsServerUrl, serviceConfig, baseDir) {
7049
7074
  if (!isNodeEnvironment()) {
7050
7075
  throw new Error(
7051
7076
  "Visual rasterization requires a Node.js environment. It is not available in browser environments."
7052
7077
  );
7053
7078
  }
7054
7079
  if (serviceConfig?.render) {
7055
- return serviceConfig.render({ presentation, dpi });
7080
+ return serviceConfig.render({ presentation, dpi, baseDir });
7056
7081
  }
7057
7082
  const serverUrl = resolveServiceUrl(
7058
7083
  propsServerUrl,
@@ -7062,7 +7087,7 @@ async function rasterize(presentation, dpi, propsServerUrl, serviceConfig) {
7062
7087
  const response = await postJsonToService({
7063
7088
  url: serverUrl,
7064
7089
  path: "/rasterize",
7065
- body: { presentation, dpi },
7090
+ body: { presentation, dpi, ...baseDir !== void 0 && { baseDir } },
7066
7091
  headers: serviceConfig?.headers,
7067
7092
  serviceLabel: "PPTX rasterization service",
7068
7093
  onUnreachable: (url, cause) => `PPTX rasterization service is not reachable at ${url}. Configure services.pptx with a \`render\` callback or a running \`serverUrl\`.
@@ -7091,12 +7116,26 @@ async function renderVisualComponent(component, theme, themeName, context) {
7091
7116
  const dpi = clampVisualDpi(
7092
7117
  props.dpi ?? serviceConfig?.dpi ?? DEFAULT_VISUAL_DPI
7093
7118
  );
7094
- const result = await rasterize(
7095
- presentation,
7096
- dpi,
7097
- props.serverUrl,
7098
- serviceConfig
7119
+ const preRasterized = context?.visualRasterResults?.get(
7120
+ visualRasterKey(
7121
+ presentation,
7122
+ dpi,
7123
+ effectiveVisualServerUrl(props, serviceConfig)
7124
+ )
7099
7125
  );
7126
+ let result;
7127
+ if (preRasterized) {
7128
+ if (!preRasterized.ok) throw new Error(preRasterized.error);
7129
+ result = preRasterized;
7130
+ } else {
7131
+ result = await rasterizeVisualSlide(
7132
+ presentation,
7133
+ dpi,
7134
+ props.serverUrl,
7135
+ serviceConfig,
7136
+ getBaseDir()
7137
+ );
7138
+ }
7100
7139
  return await createImage(
7101
7140
  result.base64DataUri,
7102
7141
  theme,
@@ -7158,6 +7197,203 @@ var textSpaceAfterComponent = createComponent({
7158
7197
 
7159
7198
  // src/core/render.ts
7160
7199
  init_docxImagePositioning();
7200
+
7201
+ // src/core/prerasterizeVisuals.ts
7202
+ import {
7203
+ clampVisualDpi as clampVisualDpi2,
7204
+ DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI2,
7205
+ MAX_RASTERIZE_BATCH_SLIDES
7206
+ } from "@json-to-office/shared";
7207
+
7208
+ // src/utils/promiseLimiter.ts
7209
+ function createLimiter(max) {
7210
+ let active = 0;
7211
+ const queue = [];
7212
+ const release = () => {
7213
+ active--;
7214
+ queue.shift()?.();
7215
+ };
7216
+ return async function limit(fn) {
7217
+ while (active >= max)
7218
+ await new Promise((resolve4) => queue.push(resolve4));
7219
+ active++;
7220
+ try {
7221
+ return await fn();
7222
+ } finally {
7223
+ release();
7224
+ }
7225
+ };
7226
+ }
7227
+
7228
+ // src/core/prerasterizeVisuals.ts
7229
+ var DEFAULT_FALLBACK_CONCURRENCY = 4;
7230
+ var BATCH_TIMEOUT_BASE_MS = 3e4;
7231
+ var BATCH_TIMEOUT_PER_SLIDE_MS = 1e4;
7232
+ function collectVisualProps(root) {
7233
+ const found = [];
7234
+ const seen = /* @__PURE__ */ new WeakSet();
7235
+ const visit = (node) => {
7236
+ if (!node || typeof node !== "object") return;
7237
+ if (seen.has(node)) return;
7238
+ seen.add(node);
7239
+ if (Array.isArray(node)) {
7240
+ for (const item of node) visit(item);
7241
+ return;
7242
+ }
7243
+ const obj = node;
7244
+ if (typeof obj.name === "string" && obj.enabled === false) return;
7245
+ if (obj.name === "visual" && obj.props && typeof obj.props === "object") {
7246
+ found.push(obj.props);
7247
+ return;
7248
+ }
7249
+ for (const value of Object.values(obj)) visit(value);
7250
+ };
7251
+ visit(root);
7252
+ return found;
7253
+ }
7254
+ function toErrorMessage(error) {
7255
+ return error instanceof Error ? error.message : String(error);
7256
+ }
7257
+ function* chunksOf(items, size) {
7258
+ for (let i = 0; i < items.length; i += size) yield items.slice(i, i + size);
7259
+ }
7260
+ function applyBatchResponse(chunk, response, map) {
7261
+ const results = response?.results;
7262
+ if (!Array.isArray(results) || results.length !== chunk.length) return false;
7263
+ const entries = [];
7264
+ for (let i = 0; i < chunk.length; i++) {
7265
+ const item = results[i];
7266
+ if (item && item.ok === true && typeof item.base64DataUri === "string" && item.base64DataUri.length > 0 && typeof item.width === "number" && typeof item.height === "number") {
7267
+ entries.push([
7268
+ chunk[i].key,
7269
+ {
7270
+ ok: true,
7271
+ base64DataUri: item.base64DataUri,
7272
+ width: item.width,
7273
+ height: item.height
7274
+ }
7275
+ ]);
7276
+ } else if (item && item.ok === false && typeof item.error === "string") {
7277
+ entries.push([chunk[i].key, { ok: false, error: item.error }]);
7278
+ } else {
7279
+ return false;
7280
+ }
7281
+ }
7282
+ for (const [key, value] of entries) map.set(key, value);
7283
+ return true;
7284
+ }
7285
+ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7286
+ const map = /* @__PURE__ */ new Map();
7287
+ if (!isNodeEnvironment() && !serviceConfig?.renderBatch && !serviceConfig?.render) {
7288
+ return map;
7289
+ }
7290
+ const visuals = collectVisualProps(root);
7291
+ if (visuals.length === 0) return map;
7292
+ const targets = /* @__PURE__ */ new Map();
7293
+ for (const props of visuals) {
7294
+ try {
7295
+ const serverUrl2 = effectiveVisualServerUrl(props, serviceConfig);
7296
+ if (serverUrl2 !== void 0) continue;
7297
+ const presentation = buildVisualPresentation(props);
7298
+ const dpi = clampVisualDpi2(
7299
+ props.dpi ?? serviceConfig?.dpi ?? DEFAULT_VISUAL_DPI2
7300
+ );
7301
+ const key = visualRasterKey(presentation, dpi, serverUrl2);
7302
+ if (!targets.has(key)) targets.set(key, { key, presentation, dpi });
7303
+ } catch {
7304
+ continue;
7305
+ }
7306
+ }
7307
+ if (targets.size === 0) return map;
7308
+ const unique = [...targets.values()];
7309
+ const limit = createLimiter(
7310
+ Math.max(1, options.concurrency ?? DEFAULT_FALLBACK_CONCURRENCY)
7311
+ );
7312
+ const rasterizeIndividually = async (chunk) => {
7313
+ await Promise.all(
7314
+ chunk.map(
7315
+ (target) => limit(async () => {
7316
+ try {
7317
+ const result = await rasterizeVisualSlide(
7318
+ target.presentation,
7319
+ target.dpi,
7320
+ void 0,
7321
+ serviceConfig,
7322
+ options.baseDir
7323
+ );
7324
+ map.set(target.key, { ok: true, ...result });
7325
+ } catch (error) {
7326
+ map.set(target.key, { ok: false, error: toErrorMessage(error) });
7327
+ }
7328
+ })
7329
+ )
7330
+ );
7331
+ };
7332
+ if (serviceConfig?.renderBatch) {
7333
+ for (const chunk of chunksOf(unique, MAX_RASTERIZE_BATCH_SLIDES)) {
7334
+ try {
7335
+ const response = await serviceConfig.renderBatch({
7336
+ slides: chunk.map((target) => ({
7337
+ presentation: target.presentation,
7338
+ dpi: target.dpi
7339
+ })),
7340
+ ...options.baseDir !== void 0 && { baseDir: options.baseDir }
7341
+ });
7342
+ if (!applyBatchResponse(chunk, response, map)) {
7343
+ throw new Error(
7344
+ "batch rasterizer returned a malformed result (expected index-aligned results[])"
7345
+ );
7346
+ }
7347
+ } catch (error) {
7348
+ if (serviceConfig.render || serviceConfig.serverUrl) {
7349
+ await rasterizeIndividually(chunk);
7350
+ } else {
7351
+ const message = toErrorMessage(error);
7352
+ for (const target of chunk) {
7353
+ map.set(target.key, { ok: false, error: message });
7354
+ }
7355
+ }
7356
+ }
7357
+ }
7358
+ return map;
7359
+ }
7360
+ if (serviceConfig?.render) {
7361
+ await rasterizeIndividually(unique);
7362
+ return map;
7363
+ }
7364
+ const serverUrl = resolveServiceUrl(
7365
+ void 0,
7366
+ serviceConfig?.serverUrl,
7367
+ DEFAULT_RASTERIZE_SERVER_URL
7368
+ );
7369
+ for (const chunk of chunksOf(unique, MAX_RASTERIZE_BATCH_SLIDES)) {
7370
+ let applied = false;
7371
+ try {
7372
+ const response = await postJsonToService({
7373
+ url: serverUrl,
7374
+ path: "/rasterize/batch",
7375
+ body: {
7376
+ slides: chunk.map((target) => ({
7377
+ presentation: target.presentation,
7378
+ dpi: target.dpi
7379
+ })),
7380
+ ...options.baseDir !== void 0 && { baseDir: options.baseDir }
7381
+ },
7382
+ headers: serviceConfig?.headers,
7383
+ timeoutMs: BATCH_TIMEOUT_BASE_MS + BATCH_TIMEOUT_PER_SLIDE_MS * chunk.length,
7384
+ serviceLabel: "PPTX batch rasterization service",
7385
+ onUnreachable: (url, cause) => `PPTX rasterization service is not reachable at ${url}. Cause: ${cause}`
7386
+ });
7387
+ applied = applyBatchResponse(chunk, await response.json(), map);
7388
+ } catch {
7389
+ applied = false;
7390
+ }
7391
+ if (!applied) await rasterizeIndividually(chunk);
7392
+ }
7393
+ return map;
7394
+ }
7395
+
7396
+ // src/core/render.ts
7161
7397
  function getAlignment3(alignment) {
7162
7398
  switch (alignment) {
7163
7399
  case "center":
@@ -7189,10 +7425,13 @@ function coreProperties(metadata) {
7189
7425
  async function renderDocument(structure, layout, options) {
7190
7426
  return runWithGenerationDate(
7191
7427
  structure.metadata.date,
7192
- () => globalBookmarkRegistry.runScoped(
7193
- () => globalRevisionIdRegistry.runScoped(
7194
- () => globalNumberingRegistry.runScoped(
7195
- () => renderDocumentScoped(structure, layout, options)
7428
+ () => runWithBaseDir(
7429
+ options?.baseDir,
7430
+ () => globalBookmarkRegistry.runScoped(
7431
+ () => globalRevisionIdRegistry.runScoped(
7432
+ () => globalNumberingRegistry.runScoped(
7433
+ () => renderDocumentScoped(structure, layout, options)
7434
+ )
7196
7435
  )
7197
7436
  )
7198
7437
  )
@@ -7211,6 +7450,21 @@ async function renderDocumentScoped(structure, layout, options) {
7211
7450
  structure.themeName
7212
7451
  );
7213
7452
  context.services = options?.services;
7453
+ try {
7454
+ const visualRasterResults = await prerasterizeVisuals(
7455
+ layout.sections,
7456
+ options?.services?.pptx,
7457
+ { baseDir: getBaseDir() }
7458
+ );
7459
+ if (visualRasterResults.size > 0) {
7460
+ context.visualRasterResults = visualRasterResults;
7461
+ }
7462
+ } catch (error) {
7463
+ console.warn(
7464
+ "[core-docx] Visual pre-rasterization failed; falling back to per-visual rasterization:",
7465
+ error instanceof Error ? error.message : error
7466
+ );
7467
+ }
7214
7468
  let sectionBookmarkCounter = 0;
7215
7469
  let previousHeader = void 0;
7216
7470
  let previousFooter = void 0;
@@ -7787,10 +8041,10 @@ function normalizeDocument(document) {
7787
8041
 
7788
8042
  // src/json/filesystem.ts
7789
8043
  import { promises as fs3 } from "fs";
7790
- import { join, resolve, isAbsolute } from "path";
8044
+ import { join, resolve as resolve2, isAbsolute as isAbsolute2 } from "path";
7791
8045
  async function loadJsonDefinition(filePath) {
7792
8046
  try {
7793
- const absolutePath = isAbsolute(filePath) ? filePath : resolve(filePath);
8047
+ const absolutePath = isAbsolute2(filePath) ? filePath : resolve2(filePath);
7794
8048
  await fs3.access(absolutePath);
7795
8049
  const fileContent = await fs3.readFile(absolutePath, "utf-8");
7796
8050
  return parseJsonWithLineNumbers(fileContent);
@@ -7865,7 +8119,8 @@ async function generateDocument(document, options) {
7865
8119
  options?.services,
7866
8120
  options?.fonts,
7867
8121
  options?.warnings,
7868
- resolveGenerationDate(options)
8122
+ resolveGenerationDate(options),
8123
+ options?.baseDir
7869
8124
  );
7870
8125
  }
7871
8126
  async function generateFromConfig(props, components, options) {
@@ -7876,7 +8131,7 @@ async function generateFromConfig(props, components, options) {
7876
8131
  };
7877
8132
  return await generateDocument(reportComponent, options);
7878
8133
  }
7879
- async function generateDocumentWithCustomThemes(documentIn, customThemes, services, fonts, warnings, generationDate) {
8134
+ async function generateDocumentWithCustomThemes(documentIn, customThemes, services, fonts, warnings, generationDate, baseDir) {
7880
8135
  const { document, theme, themeName } = resolveThemeContext(documentIn, {
7881
8136
  customThemes,
7882
8137
  fonts,
@@ -7892,7 +8147,8 @@ async function generateDocumentWithCustomThemes(documentIn, customThemes, servic
7892
8147
  const layout = applyLayout(structure.sections, theme, themeName);
7893
8148
  const renderedDocument = await renderDocument(structure, layout, {
7894
8149
  bypassCache: false,
7895
- services
8150
+ services,
8151
+ baseDir
7896
8152
  });
7897
8153
  return renderedDocument;
7898
8154
  }
@@ -7937,7 +8193,8 @@ async function generateDocumentFromJson(jsonConfig, options) {
7937
8193
  options?.services,
7938
8194
  options?.fonts,
7939
8195
  options?.warnings,
7940
- resolveGenerationDate(options)
8196
+ resolveGenerationDate(options),
8197
+ options?.baseDir
7941
8198
  );
7942
8199
  }
7943
8200
  function validateJsonSchema(jsonConfig) {
@@ -7953,7 +8210,10 @@ async function generateAndSaveFromJson(jsonConfig, filename, options) {
7953
8210
  }
7954
8211
  async function generateDocumentFromFile(filePath, options) {
7955
8212
  const jsonDefinition = await loadJsonDefinition(filePath);
7956
- return await generateDocumentFromJson(jsonDefinition, options);
8213
+ return await generateDocumentFromJson(jsonDefinition, {
8214
+ baseDir: dirname(resolve3(filePath)),
8215
+ ...options
8216
+ });
7957
8217
  }
7958
8218
  async function generateBufferFromFile(filePath, options) {
7959
8219
  const document = await generateDocumentFromFile(filePath, options);
@@ -7988,32 +8248,41 @@ var DocumentGenerator = {
7988
8248
 
7989
8249
  // src/core/flattenVisuals.ts
7990
8250
  import {
7991
- clampVisualDpi as clampVisualDpi2,
7992
- DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI2
8251
+ clampVisualDpi as clampVisualDpi3,
8252
+ DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI3
7993
8253
  } from "@json-to-office/shared";
7994
8254
  var DEFAULT_CONCURRENCY = 4;
7995
- function createLimiter(max) {
7996
- let active = 0;
7997
- const queue = [];
7998
- const release = () => {
7999
- active--;
8000
- queue.shift()?.();
8001
- };
8002
- return async function limit(fn) {
8003
- if (active >= max)
8004
- await new Promise((resolve2) => queue.push(resolve2));
8005
- active++;
8006
- try {
8007
- return await fn();
8008
- } finally {
8009
- release();
8010
- }
8011
- };
8012
- }
8013
8255
  async function flattenVisuals(doc, options) {
8256
+ let rasterize = options.rasterize;
8257
+ if (options.rasterizeBatch) {
8258
+ const preRasterized = await prerasterizeVisuals(
8259
+ doc,
8260
+ {
8261
+ render: options.rasterize,
8262
+ renderBatch: options.rasterizeBatch,
8263
+ dpi: options.dpi
8264
+ },
8265
+ { baseDir: options.baseDir, concurrency: options.concurrency }
8266
+ ).catch(() => /* @__PURE__ */ new Map());
8267
+ rasterize = async (request) => {
8268
+ const hit = preRasterized.get(
8269
+ visualRasterKey(request.presentation, request.dpi)
8270
+ );
8271
+ if (hit) {
8272
+ if (!hit.ok) throw new Error(hit.error);
8273
+ return {
8274
+ base64DataUri: hit.base64DataUri,
8275
+ width: hit.width,
8276
+ height: hit.height
8277
+ };
8278
+ }
8279
+ return options.rasterize(request);
8280
+ };
8281
+ }
8014
8282
  const ctx = {
8015
- rasterize: options.rasterize,
8283
+ rasterize,
8016
8284
  dpi: options.dpi,
8285
+ baseDir: options.baseDir,
8017
8286
  limit: createLimiter(
8018
8287
  Math.max(1, options.concurrency ?? DEFAULT_CONCURRENCY)
8019
8288
  )
@@ -8022,9 +8291,13 @@ async function flattenVisuals(doc, options) {
8022
8291
  }
8023
8292
  async function rasterizeVisual(obj, ctx) {
8024
8293
  const props = obj.props;
8025
- const dpi = clampVisualDpi2(props.dpi ?? ctx.dpi ?? DEFAULT_VISUAL_DPI2);
8294
+ const dpi = clampVisualDpi3(props.dpi ?? ctx.dpi ?? DEFAULT_VISUAL_DPI3);
8026
8295
  const result = await ctx.limit(
8027
- () => ctx.rasterize({ presentation: buildVisualPresentation(props), dpi })
8296
+ () => ctx.rasterize({
8297
+ presentation: buildVisualPresentation(props),
8298
+ dpi,
8299
+ baseDir: ctx.baseDir
8300
+ })
8028
8301
  );
8029
8302
  const image = {
8030
8303
  name: "image",
@@ -8706,7 +8979,8 @@ function createBuilderImpl(state) {
8706
8979
  fonts: state.fonts,
8707
8980
  validation: state.validation,
8708
8981
  deterministic: state.deterministic,
8709
- generatedAt: state.generatedAt
8982
+ generatedAt: state.generatedAt,
8983
+ baseDir: state.baseDir
8710
8984
  };
8711
8985
  return createBuilderImpl(
8712
8986
  newState
@@ -8805,7 +9079,8 @@ function createBuilderImpl(state) {
8805
9079
  const layout = applyLayout(structure.sections, modedTheme, themeName);
8806
9080
  const generatedDocument = await renderDocument(structure, layout, {
8807
9081
  services: state.services,
8808
- bypassCache: !state.enableCache
9082
+ bypassCache: !state.enableCache,
9083
+ baseDir: options?.baseDir ?? state.baseDir
8809
9084
  });
8810
9085
  const preservedDefinition = preserveSet ? {
8811
9086
  ...modedRoot,
@@ -8935,7 +9210,8 @@ function createDocumentGenerator(options) {
8935
9210
  fonts: options.fonts,
8936
9211
  validation: options.validation,
8937
9212
  deterministic: options.deterministic ?? true,
8938
- generatedAt: options.generatedAt
9213
+ generatedAt: options.generatedAt,
9214
+ baseDir: options.baseDir
8939
9215
  };
8940
9216
  return createBuilderImpl(initialState);
8941
9217
  }