@json-to-office/core-docx 6.8.1 → 7.0.1

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 (38) hide show
  1. package/dist/blocks/document.d.ts.map +1 -1
  2. package/dist/core/desugarExternals.d.ts.map +1 -1
  3. package/dist/core/generationContext.d.ts +2 -2
  4. package/dist/core/generationContext.d.ts.map +1 -1
  5. package/dist/core/imageResources.d.ts.map +1 -1
  6. package/dist/core/layout.d.ts +7 -0
  7. package/dist/core/layout.d.ts.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +334 -98
  11. package/dist/index.js.map +1 -1
  12. package/dist/ir/compiler.d.ts.map +1 -1
  13. package/dist/ir/types.d.ts +14 -0
  14. package/dist/ir/types.d.ts.map +1 -1
  15. package/dist/plugin/createDocumentGenerator.d.ts.map +1 -1
  16. package/dist/plugin/example/index.js +257 -59
  17. package/dist/plugin/example/index.js.map +1 -1
  18. package/dist/plugin/index.d.ts +1 -1
  19. package/dist/plugin/index.d.ts.map +1 -1
  20. package/dist/plugin/types.d.ts +27 -0
  21. package/dist/plugin/types.d.ts.map +1 -1
  22. package/dist/quality/facts.d.ts +14 -1
  23. package/dist/quality/facts.d.ts.map +1 -1
  24. package/dist/quality/rules.d.ts +2 -2
  25. package/dist/quality/rules.d.ts.map +1 -1
  26. package/dist/renderers/docxjs/emit.d.ts.map +1 -1
  27. package/dist/renderers/office-open/chartParts.d.ts.map +1 -1
  28. package/dist/renderers/office-open/emit.d.ts.map +1 -1
  29. package/dist/styles/index.d.ts +2 -1
  30. package/dist/styles/index.d.ts.map +1 -1
  31. package/dist/styles/themeToStyles.d.ts +23 -0
  32. package/dist/styles/themeToStyles.d.ts.map +1 -1
  33. package/dist/styles/utils/layoutUtils.d.ts +41 -0
  34. package/dist/styles/utils/layoutUtils.d.ts.map +1 -1
  35. package/dist/templates/themes/consulting.docx.theme.json +2 -1
  36. package/dist/templates/themes/index.d.ts +1 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -1389,7 +1389,8 @@ var init_consulting_docx_theme = __esm({
1389
1389
  },
1390
1390
  stat: {
1391
1391
  face: "heading",
1392
- weight: 700,
1392
+ size: 22,
1393
+ weight: 400,
1393
1394
  color: "accent",
1394
1395
  lineHeight: 1
1395
1396
  },
@@ -1696,7 +1697,7 @@ var init_themes = __esm({
1696
1697
  getTheme = (themeName) => {
1697
1698
  return themes[themeName];
1698
1699
  };
1699
- getThemeWithFallback = (themeName, fallbackTheme = "minimal") => {
1700
+ getThemeWithFallback = (themeName, fallbackTheme = "consulting") => {
1700
1701
  const theme = getTheme(themeName) || getTheme(fallbackTheme);
1701
1702
  if (!theme) {
1702
1703
  throw new Error(
@@ -1729,7 +1730,7 @@ function resolveTheme(theme, themeName) {
1729
1730
  if (themeName) {
1730
1731
  return getTheme(themeName);
1731
1732
  }
1732
- return getTheme("minimal");
1733
+ return getTheme("consulting");
1733
1734
  }
1734
1735
  function resolveFontFamily(theme, fontRef) {
1735
1736
  if (!theme?.fonts?.body?.family) {
@@ -1846,6 +1847,21 @@ function getPageDimensions(size) {
1846
1847
  }
1847
1848
  return { width: size.width, height: size.height };
1848
1849
  }
1850
+ function themeForSectionPage(theme, page) {
1851
+ if (!page || !page.size && !page.margins) return theme;
1852
+ const base = getPageSetup(theme);
1853
+ return {
1854
+ ...theme,
1855
+ page: {
1856
+ ...theme.page,
1857
+ size: page.size ?? theme.page?.size ?? "A4",
1858
+ margins: { ...theme.page?.margins, ...base.margin, ...page.margins }
1859
+ }
1860
+ };
1861
+ }
1862
+ function pageSizeDiffers(a, b) {
1863
+ return a.width !== b.width || a.height !== b.height;
1864
+ }
1849
1865
  var resolveTableParagraphSpacing, PAGE_SIZES, getTableStyle, getDocumentMargins, getPageSetup;
1850
1866
  var init_layoutUtils = __esm({
1851
1867
  "src/styles/utils/layoutUtils.ts"() {
@@ -3231,7 +3247,7 @@ function emitBlock(block2, resources = /* @__PURE__ */ new Map()) {
3231
3247
  }
3232
3248
  }
3233
3249
  function emitToc(block2) {
3234
- return new TableOfContents(block2.alias ?? "Table of Contents", {
3250
+ return new ThemedTableOfContents(block2.alias ?? "Table of Contents", {
3235
3251
  ...block2.hyperlink !== void 0 ? { hyperlink: block2.hyperlink } : {},
3236
3252
  ...block2.headingRange ? {
3237
3253
  headingStyleRange: `${block2.headingRange.from}-${block2.headingRange.to}`
@@ -3363,7 +3379,7 @@ function emitBorder(border3) {
3363
3379
  ...border3.spacePoints !== void 0 ? { space: border3.spacePoints } : {}
3364
3380
  };
3365
3381
  }
3366
- var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, VERTICAL_ALIGN, TABLE_ANCHOR, HORIZONTAL_POSITION, VERTICAL_POSITION, BORDER_STYLE;
3382
+ var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, ThemedTableOfContents, VERTICAL_ALIGN, TABLE_ANCHOR, HORIZONTAL_POSITION, VERTICAL_POSITION, BORDER_STYLE;
3367
3383
  var init_emit = __esm({
3368
3384
  "src/renderers/docxjs/emit.ts"() {
3369
3385
  "use strict";
@@ -3383,6 +3399,11 @@ var init_emit = __esm({
3383
3399
  tight: TextWrappingType.TIGHT,
3384
3400
  topAndBottom: TextWrappingType.TOP_AND_BOTTOM
3385
3401
  };
3402
+ ThemedTableOfContents = class extends TableOfContents {
3403
+ };
3404
+ Object.defineProperty(ThemedTableOfContents.prototype, "getTabStopsForLevel", {
3405
+ value: () => []
3406
+ });
3386
3407
  VERTICAL_ALIGN = {
3387
3408
  top: VerticalAlign.TOP,
3388
3409
  center: VerticalAlign.CENTER,
@@ -4143,13 +4164,30 @@ import {
4143
4164
  spliceChartXml
4144
4165
  } from "@json-to-office/shared/rendering";
4145
4166
  function spliceInput(chart) {
4167
+ const { bold, ...rest } = chart.textFont;
4168
+ const textFont = rest;
4169
+ const axisTitleFont = { ...rest, bold };
4170
+ const chartTitleFont = {
4171
+ ...rest,
4172
+ fontSize: Math.round(rest.fontSize * CHART_TITLE_SCALE),
4173
+ bold: true
4174
+ };
4146
4175
  return {
4147
4176
  chartType: chart.chartType,
4148
4177
  series: chart.series,
4149
4178
  colors: chart.colors,
4179
+ textFont,
4180
+ titleFont: chartTitleFont,
4150
4181
  ...chart.legendPosition ? { legendPosition: chart.legendPosition } : {},
4151
- ...chart.categoryAxisTitle ? { categoryAxis: { title: chart.categoryAxisTitle } } : {},
4152
- ...chart.valueAxisTitle ? { valueAxis: { title: chart.valueAxisTitle } } : {}
4182
+ ...chart.categoryAxisTitle ? {
4183
+ categoryAxis: {
4184
+ title: chart.categoryAxisTitle,
4185
+ titleFont: axisTitleFont
4186
+ }
4187
+ } : {},
4188
+ ...chart.valueAxisTitle ? {
4189
+ valueAxis: { title: chart.valueAxisTitle, titleFont: axisTitleFont }
4190
+ } : {}
4153
4191
  };
4154
4192
  }
4155
4193
  function declareWorkbookContentType(zip) {
@@ -4193,10 +4231,12 @@ function spliceChartParts(zip, charts) {
4193
4231
  }
4194
4232
  declareWorkbookContentType(zip);
4195
4233
  }
4234
+ var CHART_TITLE_SCALE;
4196
4235
  var init_chartParts = __esm({
4197
4236
  "src/renderers/office-open/chartParts.ts"() {
4198
4237
  "use strict";
4199
4238
  init_chartWorkbook();
4239
+ CHART_TITLE_SCALE = 1.4;
4200
4240
  }
4201
4241
  });
4202
4242
 
@@ -4742,9 +4782,6 @@ function tocEntry(entry) {
4742
4782
  return {
4743
4783
  paragraph: {
4744
4784
  style: `TOC${entry.level}`,
4745
- tabStops: [
4746
- { type: "right", position: TOC_PAGE_TAB_TWIPS, leader: "dot" }
4747
- ],
4748
4785
  children: [{ text: entry.text }, { children: [{ tab: true }] }]
4749
4786
  }
4750
4787
  };
@@ -4774,13 +4811,18 @@ function table(value, ctx) {
4774
4811
  return {
4775
4812
  rows: value.rows.map((row) => tableRow(row, ctx)),
4776
4813
  width: {
4777
- size: value.width.kind === "auto" ? 0 : value.width.value,
4814
+ // `w:w` is an integer in OOXML. A grid that splits a remainder between
4815
+ // unstated columns comes out fractional, and the XML would carry it
4816
+ // verbatim; floored here, as the docxjs backend floors it.
4817
+ size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? Math.floor(value.width.value) : value.width.value,
4778
4818
  type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
4779
4819
  },
4780
4820
  layout: value.layout,
4781
4821
  // An empty grid is a table with nothing to say about its columns, which is
4782
4822
  // not the same as one whose columns are all zero wide.
4783
- ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values } : {},
4823
+ ...value.columnGrid.values.length > 0 ? {
4824
+ columnWidths: value.columnGrid.unit === "twips" ? value.columnGrid.values.map(Math.floor) : value.columnGrid.values
4825
+ } : {},
4784
4826
  ...value.alignment ? { alignment: alignment(value.alignment) } : {},
4785
4827
  ...value.borders ? { borders: borders2(value.borders) } : {},
4786
4828
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
@@ -4972,7 +5014,7 @@ function numberingConfig2(numbering) {
4972
5014
  }))
4973
5015
  };
4974
5016
  }
4975
- var WRAP_TYPE2, BODY_ANCHOR, TOC_PAGE_TAB_TWIPS;
5017
+ var WRAP_TYPE2, BODY_ANCHOR;
4976
5018
  var init_emit2 = __esm({
4977
5019
  "src/renderers/office-open/emit.ts"() {
4978
5020
  "use strict";
@@ -4988,7 +5030,6 @@ var init_emit2 = __esm({
4988
5030
  middle: "ctr",
4989
5031
  bottom: "b"
4990
5032
  };
4991
- TOC_PAGE_TAB_TWIPS = 9025;
4992
5033
  }
4993
5034
  });
4994
5035
 
@@ -5269,7 +5310,9 @@ function addPngTextChunk(data, marker) {
5269
5310
  }
5270
5311
  offset = chunkEnd;
5271
5312
  }
5272
- return Buffer.concat([data, marker]);
5313
+ throw new Error(
5314
+ "Cannot mark a PNG that has no intact IEND chunk; its bytes were not checked to decode before rendering"
5315
+ );
5273
5316
  }
5274
5317
  function addJpegComment(data, marker) {
5275
5318
  if (data.length < 2 || data[0] !== 255 || data[1] !== 216) {
@@ -5968,6 +6011,7 @@ function containsComponent(root, name) {
5968
6011
  }
5969
6012
 
5970
6013
  // src/core/desugarExternals.ts
6014
+ init_layoutUtils();
5971
6015
  import {
5972
6016
  clampVisualDpi as clampVisualDpi2,
5973
6017
  DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI2,
@@ -6171,8 +6215,18 @@ async function desugarExternals(document, options) {
6171
6215
  }
6172
6216
  ).catch(() => /* @__PURE__ */ new Map());
6173
6217
  const chartCache = /* @__PURE__ */ new Map();
6174
- return transformComponents(document, async (node) => {
6218
+ const replace = (theme, inSection) => async function replaceNode(node) {
6175
6219
  if (node.enabled === false) return void 0;
6220
+ if (node.name === "section" && !inSection) {
6221
+ const page = node.props.page;
6222
+ return transformComponents(
6223
+ node,
6224
+ replace(themeForSectionPage(theme, page), true)
6225
+ );
6226
+ }
6227
+ return replaceExternal(node, theme);
6228
+ };
6229
+ const replaceExternal = async (node, theme) => {
6176
6230
  if (node.name === "visual") {
6177
6231
  if (isNativeVisualProps2(node.props)) return void 0;
6178
6232
  return withNodeIdentity(node, {
@@ -6190,7 +6244,7 @@ async function desugarExternals(document, options) {
6190
6244
  name: "image",
6191
6245
  props: await renderChartToImageProps(
6192
6246
  node.props,
6193
- options.theme,
6247
+ theme,
6194
6248
  options.services?.highcharts,
6195
6249
  options.chartFonts,
6196
6250
  options.warnings,
@@ -6199,7 +6253,8 @@ async function desugarExternals(document, options) {
6199
6253
  });
6200
6254
  }
6201
6255
  return void 0;
6202
- });
6256
+ };
6257
+ return transformComponents(document, replace(options.theme, false));
6203
6258
  }
6204
6259
  async function visualImageProps(props, prerastered, options) {
6205
6260
  const serviceConfig = options.services?.pptx;
@@ -6234,6 +6289,7 @@ init_imageUtils();
6234
6289
  import {
6235
6290
  isNativeVisualProps as isNativeVisualProps3
6236
6291
  } from "@json-to-office/shared-docx";
6292
+ import { imageIntegrityDefect } from "@json-to-office/shared/images/node";
6237
6293
  async function loadImageResources(components) {
6238
6294
  const sources = /* @__PURE__ */ new Set();
6239
6295
  for (const component of components) collectSources(component, sources);
@@ -6243,6 +6299,8 @@ async function loadImageResources(components) {
6243
6299
  [...sources].map(async (source) => {
6244
6300
  try {
6245
6301
  const result = await getImageBuffer(source);
6302
+ const defect = imageIntegrityDefect(result.buffer);
6303
+ if (defect) throw new Error(defect);
6246
6304
  const intrinsic = readIntrinsic(result.buffer, source);
6247
6305
  loaded.set(source, {
6248
6306
  bytes: result.buffer,
@@ -6843,7 +6901,12 @@ function resolveColors(entries, at, deps) {
6843
6901
  init_themes();
6844
6902
  init_colorUtils();
6845
6903
  init_styleHelpers();
6846
- import { capsFormatting, synthesizeFamilyName } from "@json-to-office/shared";
6904
+ import {
6905
+ capsFormatting,
6906
+ designCanvas,
6907
+ synthesizeFamilyName,
6908
+ typeScaleSizes
6909
+ } from "@json-to-office/shared";
6847
6910
  var RIGHT_MARGIN_TAB_TWIPS = 9026 * 2;
6848
6911
  var STATISTIC_NUMBER_STYLE_ID = "StatisticNumber";
6849
6912
  var STATISTIC_DESCRIPTION_STYLE_ID = "StatisticDescription";
@@ -6852,6 +6915,27 @@ var STATISTIC_SIZE_POINTS = {
6852
6915
  medium: 28,
6853
6916
  large: 40
6854
6917
  };
6918
+ function statisticSizes(theme, size) {
6919
+ const sizeOf = (style) => {
6920
+ const value = theme.styles?.[style]?.size;
6921
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
6922
+ };
6923
+ const role = sizeOf("stat");
6924
+ const medium = sizeOf(STATISTIC_NUMBER_STYLE_ID) ?? role ?? STATISTIC_SIZE_POINTS.medium;
6925
+ const scale = role === void 0 ? void 0 : theme.typography?.scale?.[designCanvas("docx", theme.page?.size)];
6926
+ const steps = scale ? typeScaleSizes(scale) : [];
6927
+ const below = [...steps].reverse().find((step) => step < medium - 0.01);
6928
+ const above = steps.find((step) => step > medium + 0.01);
6929
+ const number = size === "small" ? role !== void 0 && below !== void 0 ? below : STATISTIC_SIZE_POINTS.small : size === "large" ? role !== void 0 && above !== void 0 ? above : STATISTIC_SIZE_POINTS.large : medium;
6930
+ const label = sizeOf("label");
6931
+ return {
6932
+ number,
6933
+ medium,
6934
+ // Half-points in the document, floored at 6pt.
6935
+ suffix: label ?? Math.max(12, Math.round(number)) / 2,
6936
+ description: sizeOf(STATISTIC_DESCRIPTION_STYLE_ID) ?? label ?? 10
6937
+ };
6938
+ }
6855
6939
  function getStyleSafe(theme, styleName) {
6856
6940
  return theme.styles && Object.hasOwn(theme.styles, styleName) ? theme.styles[styleName] : void 0;
6857
6941
  }
@@ -7027,8 +7111,8 @@ function chromeRun(fontProps, theme, sizeHalfPoints) {
7027
7111
  ...fontProps.italic !== void 0 && { italic: fontProps.italic }
7028
7112
  };
7029
7113
  }
7030
- function createDocumentStyles(themeNameOrObject = "minimal", language) {
7031
- const theme = typeof themeNameOrObject === "string" ? getTheme(themeNameOrObject) || getTheme("minimal") : themeNameOrObject;
7114
+ function createDocumentStyles(themeNameOrObject = "consulting", language) {
7115
+ const theme = typeof themeNameOrObject === "string" ? getTheme(themeNameOrObject) || getTheme("consulting") : themeNameOrObject;
7032
7116
  const paragraphStyles = [
7033
7117
  // Normal body text style
7034
7118
  {
@@ -7499,14 +7583,19 @@ function createDocumentStyles(themeNameOrObject = "minimal", language) {
7499
7583
  };
7500
7584
  }
7501
7585
  function createStatisticStyles(theme) {
7586
+ const sizes = statisticSizes(theme, "medium");
7587
+ const stat = theme.styles?.stat;
7588
+ const faces = ["heading", "body", "mono", "light"];
7589
+ const face = faces.find((candidate) => candidate === stat?.font) ?? "heading";
7590
+ const weight = typeof stat?.fontWeight === "number" ? stat.fontWeight : void 0;
7502
7591
  const definitions = [
7503
7592
  {
7504
7593
  id: STATISTIC_NUMBER_STYLE_ID,
7505
7594
  name: "Statistic Number",
7506
- font: "heading",
7507
- sizePoints: STATISTIC_SIZE_POINTS.medium,
7508
- color: theme.colors.primary,
7509
- bold: true,
7595
+ font: face,
7596
+ sizePoints: sizes.medium,
7597
+ color: typeof stat?.color === "string" ? stat.color : theme.colors.primary,
7598
+ bold: weight === void 0 ? true : weight >= 600,
7510
7599
  // Space above separates the figure from whatever preceded it; none below,
7511
7600
  // because the description is the other half of the same block.
7512
7601
  spacing: { before: 12, after: 0 },
@@ -7519,7 +7608,7 @@ function createStatisticStyles(theme) {
7519
7608
  id: STATISTIC_DESCRIPTION_STYLE_ID,
7520
7609
  name: "Statistic Description",
7521
7610
  font: "body",
7522
- sizePoints: 10,
7611
+ sizePoints: sizes.description,
7523
7612
  color: theme.colors.textMuted,
7524
7613
  bold: false,
7525
7614
  spacing: { before: 0, after: 12 },
@@ -9138,8 +9227,25 @@ function resolveSectionChrome(sections) {
9138
9227
  });
9139
9228
  }
9140
9229
  function compileSection(section2, index, ordinal, chrome, ctx) {
9230
+ const documentTheme = ctx.theme;
9231
+ ctx.theme = themeForSectionPage(documentTheme, section2.pageOverride);
9232
+ try {
9233
+ return compileSectionBody(section2, index, ordinal, chrome, ctx);
9234
+ } finally {
9235
+ ctx.theme = documentTheme;
9236
+ }
9237
+ }
9238
+ function compileSectionBody(section2, index, ordinal, chrome, ctx) {
9141
9239
  const path4 = `sections[${index}]`;
9142
9240
  const { page, column } = section2.properties;
9241
+ if (section2.forcedPageBreak) {
9242
+ warnOnce(
9243
+ ctx,
9244
+ "section",
9245
+ `${path4} is set to continue on the page but changes the paper size or orientation, which a continuous section break cannot do, so it starts on a new page instead. Set pageBreak: true on the section to say so, or keep the page size of the section before it.`,
9246
+ "W_SECTION_PAGE_BREAK_FORCED"
9247
+ );
9248
+ }
9143
9249
  if (column && column.count > 1) {
9144
9250
  ctx.features.require("columns", `${path4}.properties.columns`);
9145
9251
  }
@@ -10091,9 +10197,10 @@ function compileStatistic(component, scope) {
10091
10197
  );
10092
10198
  }
10093
10199
  const text = (value) => normalizeUnicodeText(String(value ?? ""));
10094
- const numberPoints = STATISTIC_SIZE_POINTS[String(props.size)] ?? STATISTIC_SIZE_POINTS.medium;
10095
- const resized = numberPoints !== STATISTIC_SIZE_POINTS.medium;
10096
- const suffixHalfPoints = Math.max(12, Math.round(numberPoints));
10200
+ const sizes = statisticSizes(ctx.theme, props.size);
10201
+ const numberPoints = sizes.number;
10202
+ const resized = numberPoints !== sizes.medium;
10203
+ const suffixHalfPoints = Math.round(sizes.suffix * 2);
10097
10204
  const numberRuns = [];
10098
10205
  const numberText = text(props.number);
10099
10206
  if (numberText) {
@@ -10993,6 +11100,24 @@ function compileImage2(component, scope) {
10993
11100
  return blocks;
10994
11101
  }
10995
11102
  var DEFAULT_CHART_HEIGHT_INCHES = 3;
11103
+ var MAX_CHART_TEXT_POINTS = 10;
11104
+ function chartTextFont(theme) {
11105
+ const bodySize = getThemeFonts(theme).body?.size;
11106
+ let color = "000000";
11107
+ try {
11108
+ color = resolveColor("textPrimary", theme);
11109
+ } catch {
11110
+ }
11111
+ return {
11112
+ fontFamily: resolveFontFamily(theme, "body"),
11113
+ fontSize: Math.min(
11114
+ typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
11115
+ MAX_CHART_TEXT_POINTS
11116
+ ),
11117
+ bold: false,
11118
+ color
11119
+ };
11120
+ }
10996
11121
  function compileChart(component, scope) {
10997
11122
  const { ctx, path: path4 } = scope;
10998
11123
  const props = component.props ?? {};
@@ -11061,6 +11186,7 @@ function compileChart(component, scope) {
11061
11186
  ...props.legendPos ? { legendPosition: props.legendPos } : {},
11062
11187
  ...typeof props.catAxisTitle === "string" ? { categoryAxisTitle: props.catAxisTitle } : {},
11063
11188
  ...typeof props.valAxisTitle === "string" ? { valueAxisTitle: props.valAxisTitle } : {},
11189
+ textFont: chartTextFont(ctx.theme),
11064
11190
  ...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {},
11065
11191
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path4) } : {}
11066
11192
  };
@@ -11799,6 +11925,18 @@ init_styles();
11799
11925
  init_widthUtils();
11800
11926
  function applyLayout(sections, theme, themeName) {
11801
11927
  const layoutSections = [];
11928
+ const push = (layoutSection) => {
11929
+ const previous = layoutSections[layoutSections.length - 1];
11930
+ if (previous && layoutSection.properties.type === "continuous" && pageSizeDiffers(
11931
+ previous.properties.page.size,
11932
+ layoutSection.properties.page.size
11933
+ )) {
11934
+ layoutSection.properties.type = "nextPage";
11935
+ layoutSection.breakBefore = true;
11936
+ layoutSection.forcedPageBreak = true;
11937
+ }
11938
+ layoutSections.push(layoutSection);
11939
+ };
11802
11940
  for (let i = 0; i < sections.length; i++) {
11803
11941
  const section2 = sections[i];
11804
11942
  const layoutGroups = analyzeLayoutGroups(section2.components);
@@ -11807,7 +11945,7 @@ function applyLayout(sections, theme, themeName) {
11807
11945
  const isFirstSection = i === 0;
11808
11946
  const breakBefore = isFirstSection || Boolean(section2.pageBreak);
11809
11947
  const sectionType2 = breakBefore ? "nextPage" : "continuous";
11810
- layoutSections.push({
11948
+ push({
11811
11949
  properties: createSectionProperties(
11812
11950
  getColumnSettings("single"),
11813
11951
  theme,
@@ -11829,7 +11967,12 @@ function applyLayout(sections, theme, themeName) {
11829
11967
  for (let j = 0; j < layoutGroups.length; j++) {
11830
11968
  const group = layoutGroups[j];
11831
11969
  const isSingleColumnsGroup = group.components.length === 1 && isColumnsComponent(group.components[0]);
11832
- const columnSettings = isSingleColumnsGroup ? createColumnSettingsFromConfig(group.components[0], theme, themeName) : getColumnSettings(group.layout);
11970
+ const columnSettings = isSingleColumnsGroup ? createColumnSettingsFromConfig(
11971
+ group.components[0],
11972
+ // Percentages are of this section's text measure, not the theme's.
11973
+ theme ? themeForSectionPage(theme, section2.page) : theme,
11974
+ themeName
11975
+ ) : getColumnSettings(group.layout);
11833
11976
  const isFirstSection = i === 0 && j === 0;
11834
11977
  const titlelessSectionBreak = Boolean(section2.pageBreak && j === 0);
11835
11978
  const breakBefore = group.breakBefore || isFirstSection || titlelessSectionBreak;
@@ -11837,7 +11980,7 @@ function applyLayout(sections, theme, themeName) {
11837
11980
  const contentComponents = isSingleColumnsGroup ? processLayoutComponents(
11838
11981
  group.components[0].children || []
11839
11982
  ) : processLayoutComponents(group.components);
11840
- layoutSections.push({
11983
+ push({
11841
11984
  properties: createSectionProperties(
11842
11985
  columnSettings,
11843
11986
  theme,
@@ -12384,10 +12527,10 @@ import {
12384
12527
  } from "@json-to-office/quality";
12385
12528
  import { DEFAULT_DOCX_RENDERER_ID as DEFAULT_DOCX_RENDERER_ID2 } from "@json-to-office/shared-docx";
12386
12529
  import {
12387
- designCanvas as designCanvas2,
12530
+ designCanvas as designCanvas3,
12388
12531
  designColors as designColors2,
12389
12532
  resolveDesignColor as resolveDesignColor2,
12390
- typeScaleSizes
12533
+ typeScaleSizes as typeScaleSizes2
12391
12534
  } from "@json-to-office/shared";
12392
12535
 
12393
12536
  // src/core/generationContext.ts
@@ -12443,7 +12586,7 @@ function applyThemeOverrides(theme, overrides) {
12443
12586
  // src/themes/design-system.ts
12444
12587
  init_defaults();
12445
12588
  import {
12446
- designCanvas,
12589
+ designCanvas as designCanvas2,
12447
12590
  resolveMotif,
12448
12591
  resolveTypeRoles,
12449
12592
  validateDesignColors
@@ -12453,7 +12596,7 @@ function resolveDocxDesignSystem(theme) {
12453
12596
  const motif = resolveMotif(theme.motif);
12454
12597
  if (!theme.typography && !theme.spacing)
12455
12598
  return motif === theme.motif ? theme : withMotif(theme, motif);
12456
- const canvas = designCanvas("docx", theme.page.size);
12599
+ const canvas = designCanvas2("docx", theme.page.size);
12457
12600
  const roles = resolveTypeRoles(theme, canvas, theme.fonts.body.size ?? 11);
12458
12601
  const styles = { ...theme.styles };
12459
12602
  for (const [name, role] of Object.entries(roles)) {
@@ -12538,7 +12681,7 @@ function resolveThemeContext(documentIn, options = {}) {
12538
12681
  }
12539
12682
  const document = documentIn.props === void 0 ? { ...documentIn, props: {} } : documentIn;
12540
12683
  const authoredThemeName = document.props.theme || void 0;
12541
- const baseThemeName = authoredThemeName ?? "minimal";
12684
+ const baseThemeName = authoredThemeName ?? "consulting";
12542
12685
  let theme = resolveNamedTheme ? resolveNamedTheme(
12543
12686
  baseThemeName,
12544
12687
  warnings,
@@ -12902,7 +13045,8 @@ async function expandBlocksWithPlugins(document, theme, plugins, render, preserv
12902
13045
  document: second.standard,
12903
13046
  preserved: first.preserved,
12904
13047
  sourceMap: evaluator.sourceMap,
12905
- blocks: evaluator.blocks
13048
+ blocks: evaluator.blocks,
13049
+ pluginOutputs: evaluator.pluginOutputs
12906
13050
  };
12907
13051
  }
12908
13052
 
@@ -13713,10 +13857,7 @@ function textSizeFact(node, props, path4, typography, role) {
13713
13857
  };
13714
13858
  }
13715
13859
  function statisticSizeFacts(props, path4, typography) {
13716
- const named = STATISTIC_SIZE_POINTS[String(props.size)];
13717
- const styled = (id) => finiteNumber(asRecord2(typography.styles[id])?.size);
13718
- const numberPoints = named ?? STATISTIC_SIZE_POINTS.medium;
13719
- const painted = props.size === "small" || props.size === "large" ? numberPoints : styled("StatisticNumber") ?? numberPoints;
13860
+ const sizes = statisticSizes(typography.theme, props.size);
13720
13861
  const present = (value) => value !== void 0 && value !== null && String(value).trim() !== "";
13721
13862
  const facts = [];
13722
13863
  const add = (suffix, role, fontSizePt) => facts.push({
@@ -13727,19 +13868,11 @@ function statisticSizeFacts(props, path4, typography) {
13727
13868
  fontSizePt,
13728
13869
  authored: false
13729
13870
  });
13730
- if (present(props.number)) add("number", "statistic", painted);
13871
+ if (present(props.number)) add("number", "statistic", sizes.number);
13731
13872
  if (present(props.unit) || present(props.trend) || present(props.trendValue))
13732
- add(
13733
- "suffix",
13734
- "statistic-suffix",
13735
- Math.max(12, Math.round(numberPoints)) / 2
13736
- );
13873
+ add("suffix", "statistic-suffix", sizes.suffix);
13737
13874
  if (present(props.description))
13738
- add(
13739
- "description",
13740
- "statistic-description",
13741
- styled("StatisticDescription") ?? 10
13742
- );
13875
+ add("description", "statistic-description", sizes.description);
13743
13876
  return facts;
13744
13877
  }
13745
13878
  function tocSizeFacts(props, path4, typography) {
@@ -13841,8 +13974,8 @@ function prepareDocxQualityDocument(document, options = {}) {
13841
13974
  fonts: options.fonts,
13842
13975
  warnings: options.warnings
13843
13976
  });
13844
- const expanded = expandBlocks(themed.document, themed.theme);
13845
- const context = expanded.blocks.length > 0 ? { ...themed, document: expanded.document } : themed;
13977
+ const expanded = options.expanded ?? expandBlocks(themed.document, themed.theme);
13978
+ const context = options.expanded !== void 0 || expanded.blocks.length > 0 ? { ...themed, document: expanded.document } : themed;
13846
13979
  const resolved = resolveDocumentTree(context.document, context.theme);
13847
13980
  const basePage = pageBox(resolved.theme, context.themeName);
13848
13981
  const typography = {
@@ -13854,7 +13987,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13854
13987
  let previousHeadingLevel;
13855
13988
  const authoredPath = (path4) => toAuthoredPointer(
13856
13989
  themed.sourceMap ?? {},
13857
- toAuthoredPointer(expanded.sourceMap, path4)
13990
+ toAuthoredPointer(expanded.sourceMap, path4, expanded.pluginOutputs)
13858
13991
  );
13859
13992
  const addFact = (raw) => {
13860
13993
  const fact = {
@@ -13993,7 +14126,7 @@ function prepareDocxQualityDocument(document, options = {}) {
13993
14126
  );
13994
14127
  if (size) roleSizesPt[key] = size.fontSizePt;
13995
14128
  }
13996
- const scale = resolved.theme.typography?.scale?.[designCanvas2("docx", resolved.theme.page?.size)];
14129
+ const scale = resolved.theme.typography?.scale?.[designCanvas3("docx", resolved.theme.page?.size)];
13997
14130
  const typeScalePt = [
13998
14131
  .../* @__PURE__ */ new Set([
13999
14132
  ...Object.values(roleSizesPt),
@@ -14001,7 +14134,7 @@ function prepareDocxQualityDocument(document, options = {}) {
14001
14134
  const size = resolveFontSize(resolved.theme, role);
14002
14135
  return size === void 0 ? [] : [size];
14003
14136
  }),
14004
- ...scale ? typeScaleSizes(scale) : []
14137
+ ...scale ? typeScaleSizes2(scale) : []
14005
14138
  ])
14006
14139
  ].sort((a, b) => a - b);
14007
14140
  addFact({
@@ -14157,6 +14290,7 @@ function prepareDocxQualityDocument(document, options = {}) {
14157
14290
  for (const fact of svgTextFacts(props, path4)) addFact(fact);
14158
14291
  const drawn = drawnRatio(props);
14159
14292
  const natural = readableRatio(props);
14293
+ const authored = authoredPropsAt(path4);
14160
14294
  addFact({
14161
14295
  id: `docx:image:${path4}`,
14162
14296
  kind: "docx/image",
@@ -14164,7 +14298,13 @@ function prepareDocxQualityDocument(document, options = {}) {
14164
14298
  ...typeof props.alt === "string" && props.alt.trim() !== "" && { alt: props.alt },
14165
14299
  captioned: isCaptioned(facts, authoredPath(path4)),
14166
14300
  ...drawn !== void 0 && { drawnRatio: drawn },
14167
- ...natural !== void 0 && { naturalRatio: natural }
14301
+ ...natural !== void 0 && { naturalRatio: natural },
14302
+ ...authoredPath(path4) === path4 && finiteNumber(authored?.width) !== void 0 && finiteNumber(authored?.height) !== void 0 && {
14303
+ authoredSizePx: {
14304
+ width: authored.width,
14305
+ height: authored.height
14306
+ }
14307
+ }
14168
14308
  });
14169
14309
  }
14170
14310
  if (node.name === "heading") {
@@ -14730,6 +14870,8 @@ import {
14730
14870
  // src/quality/rules.ts
14731
14871
  import {
14732
14872
  chartInfoDesignFindings,
14873
+ configurationLabel,
14874
+ configurationSource,
14733
14875
  DEFAULT_MAXIMUM_CHART_SERIES,
14734
14876
  DEFAULT_MAXIMUM_PIE_SLICES,
14735
14877
  fontCountFinding,
@@ -15329,18 +15471,21 @@ var docxRequiredChromeRule = {
15329
15471
  if (required.length === 0) return [];
15330
15472
  return facts.filter(
15331
15473
  (fact) => fact.kind === "docx/chrome-slot"
15332
- ).filter((fact) => required.includes(fact.role) && !fact.present).map((fact) => ({
15333
- path: fact.path,
15334
- relatedPaths: [fact.invocation],
15335
- message: `${fact.block} states no ${fact.role} in its "${fact.slot}" slot; the ${profile?.id ?? "selected"} profile expects one on every ${fact.block}.`,
15336
- suggestion: `Fill the "${fact.slot}" slot. The theme already styles it.`,
15337
- context: { block: fact.block, slot: fact.slot, role: fact.role },
15338
- evidence: {
15339
- actual: "empty",
15340
- expected: fact.role,
15341
- values: { source: "profile", required }
15342
- }
15343
- }));
15474
+ ).filter((fact) => required.includes(fact.role) && !fact.present).map((fact) => {
15475
+ const source = configurationSource(configuration, "required");
15476
+ return {
15477
+ path: fact.path,
15478
+ relatedPaths: [fact.invocation],
15479
+ message: `${fact.block} states no ${fact.role} in its "${fact.slot}" slot; ${configurationLabel(source, profile)} expects one on every ${fact.block}.`,
15480
+ suggestion: `Fill the "${fact.slot}" slot. The theme already styles it.`,
15481
+ context: { block: fact.block, slot: fact.slot, role: fact.role },
15482
+ evidence: {
15483
+ actual: "empty",
15484
+ expected: fact.role,
15485
+ values: { source, required }
15486
+ }
15487
+ };
15488
+ });
15344
15489
  }
15345
15490
  };
15346
15491
  var docxExhibitRequiredRule = {
@@ -15401,16 +15546,21 @@ var docxRunningHeadRule = {
15401
15546
  const missing = required.filter((part) => !fact[part]);
15402
15547
  if (missing.length === 0) return [];
15403
15548
  const parts = missing.map((part) => part === "pageNumber" ? "page-number field" : part).join(", ");
15549
+ const source = configurationSource(
15550
+ configuration,
15551
+ "required",
15552
+ "fromSection"
15553
+ );
15404
15554
  return [
15405
15555
  {
15406
15556
  path: fact.path,
15407
- message: `Section ${fact.index + 1} carries no ${parts}; the ${profile?.id ?? "selected"} profile expects a running head on every section after the cover.`,
15557
+ message: `Section ${fact.index + 1} carries no ${parts}; ${configurationLabel(source, profile)} expects a running head ` + (from === 0 ? "on every section." : `on every section from section ${from + 1} on.`),
15408
15558
  suggestion: "Invoke a running-head block at the top of the first body section: its section effect fills every later section with the title and n / N.",
15409
15559
  context: { section: fact.index, missing },
15410
15560
  evidence: {
15411
15561
  actual: required.filter((part) => fact[part]),
15412
15562
  expected: required,
15413
- values: { source: "profile", fromSection: from }
15563
+ values: { source, fromSection: from }
15414
15564
  }
15415
15565
  }
15416
15566
  ];
@@ -15467,13 +15617,16 @@ var docxSizeCountRule = {
15467
15617
  formats: ["docx"],
15468
15618
  defaultEnabled: false,
15469
15619
  defaultParameters: { maximumSizes: 8 },
15470
- evaluate: ({ facts, configuration, profile }) => sizeCountFinding(
15471
- paintedSizes(facts),
15472
- numberParameter(configuration.parameters, "maximumSizes", 8),
15473
- themeFact(facts)?.path ?? "/props",
15474
- profile?.id,
15475
- DOCX_TYPE_VOCABULARY
15476
- )
15620
+ evaluate: ({ facts, configuration, profile }) => {
15621
+ const source = configurationSource(configuration, "maximumSizes");
15622
+ return sizeCountFinding(
15623
+ paintedSizes(facts),
15624
+ numberParameter(configuration.parameters, "maximumSizes", 8),
15625
+ themeFact(facts)?.path ?? "/props",
15626
+ { source, label: configurationLabel(source, profile) },
15627
+ DOCX_TYPE_VOCABULARY
15628
+ );
15629
+ }
15477
15630
  };
15478
15631
  var docxRoleDriftRule = {
15479
15632
  id: "docx/role-drift",
@@ -15530,7 +15683,12 @@ var docxBodyMeasureRule = {
15530
15683
  actual: fact.charactersPerLine,
15531
15684
  expected: wide ? maximum : minimum,
15532
15685
  unit: "characters",
15533
- values: { source: "profile" }
15686
+ values: {
15687
+ source: configurationSource(
15688
+ configuration,
15689
+ wide ? "maximumCharacters" : "minimumCharacters"
15690
+ )
15691
+ }
15534
15692
  }
15535
15693
  };
15536
15694
  });
@@ -15578,7 +15736,12 @@ var docxSectionContentRule = {
15578
15736
  actual: 0,
15579
15737
  expected: 1,
15580
15738
  unit: "headings",
15581
- values: { source: "profile" }
15739
+ values: {
15740
+ source: configurationSource(
15741
+ configuration,
15742
+ "minimumWordsForHeading"
15743
+ )
15744
+ }
15582
15745
  }
15583
15746
  }
15584
15747
  ];
@@ -15595,7 +15758,7 @@ var docxHeadingKeepNextRule = {
15595
15758
  defaultCertainty: "deterministic",
15596
15759
  formats: ["docx"],
15597
15760
  defaultEnabled: false,
15598
- evaluate: ({ facts }) => facts.filter(
15761
+ evaluate: ({ facts, configuration }) => facts.filter(
15599
15762
  (fact) => fact.kind === "docx/heading" && !fact.keepNext
15600
15763
  ).map((fact) => {
15601
15764
  const heading = /\/props\/level$/.test(fact.path) ? fact.path.replace(/\/props\/level$/, "") : void 0;
@@ -15604,7 +15767,7 @@ var docxHeadingKeepNextRule = {
15604
15767
  message: "This heading is not bound to the content under it: a page break can leave it alone at the foot of a page.",
15605
15768
  suggestion: heading ? "Set keepNext on the heading, or give the theme\u2019s heading style keepNext so every level is bound." : "Give the theme\u2019s heading style keepNext, or set it in the block definition that draws this heading.",
15606
15769
  context: { level: fact.level },
15607
- evidence: { values: { source: "profile" } },
15770
+ evidence: { values: { source: configurationSource(configuration) } },
15608
15771
  ...heading && {
15609
15772
  fixes: [
15610
15773
  {
@@ -15657,7 +15820,20 @@ var docxImageAspectRule = {
15657
15820
  {
15658
15821
  path: fact.path,
15659
15822
  drawn: fact.drawnRatio,
15660
- natural: fact.naturalRatio
15823
+ natural: fact.naturalRatio,
15824
+ ...fact.authoredSizePx && {
15825
+ sides: {
15826
+ width: {
15827
+ path: `${fact.path}/props/width`,
15828
+ value: fact.authoredSizePx.width
15829
+ },
15830
+ height: {
15831
+ path: `${fact.path}/props/height`,
15832
+ value: fact.authoredSizePx.height
15833
+ },
15834
+ decimals: 0
15835
+ }
15836
+ }
15661
15837
  },
15662
15838
  "page",
15663
15839
  tolerance
@@ -15686,17 +15862,18 @@ var docxContentsRule = {
15686
15862
  (fact) => fact.kind === "docx/outline"
15687
15863
  );
15688
15864
  if (!outline || outline.contents || outline.headings < minimum) return [];
15865
+ const source = configurationSource(configuration, "minimumHeadings");
15689
15866
  return [
15690
15867
  {
15691
15868
  path: outline.path,
15692
- message: `The document carries ${outline.headings} headings and no table of contents; the ${profile?.id ?? "selected"} profile expects one from ${minimum}.`,
15869
+ message: `The document carries ${outline.headings} headings and no table of contents; ${configurationLabel(source, profile)} expects one from ${minimum}.`,
15693
15870
  suggestion: "Add a `toc` component after the cover. Word fills it from the headings already there.",
15694
15871
  context: { headings: outline.headings, minimum },
15695
15872
  evidence: {
15696
15873
  actual: outline.headings,
15697
15874
  expected: minimum,
15698
15875
  unit: "headings",
15699
- values: { source: "profile" }
15876
+ values: { source }
15700
15877
  }
15701
15878
  }
15702
15879
  ];
@@ -15741,7 +15918,7 @@ var DOCX_QUALITY_PROFILES = {
15741
15918
  "client-report": {
15742
15919
  id: "client-report",
15743
15920
  formats: ["docx"],
15744
- description: "Client or public-administration report: a running head with page numbers on every section after the cover, a takeaway and a source wherever a block declares them, no heading skipped, every size on the theme scale with at most eleven in play, a readable measure, no empty or untitled section, every heading bound to what follows and every figure named, no page rendered empty or left half blank under the running head, and at least one chart or table.",
15921
+ description: "Client or public-administration report: a running head with page numbers on every section after the cover, a takeaway and a source wherever a block declares them, no heading skipped, every size on the theme scale with at most nine in play, a readable measure, no empty or untitled section, every heading bound to what follows and every figure named, no page rendered empty or left half blank under the running head, and at least one chart or table.",
15745
15922
  rules: {
15746
15923
  "docx/required-chrome": {
15747
15924
  parameters: { required: ["takeaway", "source"] }
@@ -15754,14 +15931,15 @@ var DOCX_QUALITY_PROFILES = {
15754
15931
  },
15755
15932
  "docx/heading-hierarchy": { severity: "warning" },
15756
15933
  "docx/type-scale": { enabled: true },
15757
- // Measured 2026-09-15 with statistics, lists and contents counted: a
15758
- // client report built from the house blocks paints up to eleven sizes —
15759
- // body, heading 1 and 2, subtitle, display, table cells and headers,
15760
- // source and chrome, and the kpi-row's figure, its unit and its label
15761
- // (28, 14 and 10pt, or 20 and 10 in a row of four). Every one of the
15762
- // reports of the #360 checkpoint and the #409 verification set came in
15763
- // at eleven or fewer; a size past the house set is an ad-hoc one.
15764
- "docx/size-count": { enabled: true, parameters: { maximumSizes: 11 } },
15934
+ // Measured 2026-09-16 with statistics, lists and contents counted, once
15935
+ // a statistic paints in the theme's roles (#454): a client report built
15936
+ // from the house blocks paints up to nine sizes 8 (source, chrome),
15937
+ // 9 (labels, the kpi-row's unit and caption), 9.5 (table cells), 10.5
15938
+ // (body), 12.5 and 16 (headings), 13 (subtitle), 18 (a four-up kpi
15939
+ // figure) and 22 (display, the kpi figure). The 155 client reports of
15940
+ // the checkpoint, host-study and verification runs came in at nine or
15941
+ // fewer; a size past the house set is an ad-hoc one.
15942
+ "docx/size-count": { enabled: true, parameters: { maximumSizes: 9 } },
15765
15943
  "docx/role-drift": { enabled: true },
15766
15944
  // The report blocks place every figure in a captioned block, so a page
15767
15945
  // with no text under the running head is a stray break or an empty
@@ -17811,7 +17989,7 @@ function createBuilderImpl(state) {
17811
17989
  }
17812
17990
  return new Set(list);
17813
17991
  }
17814
- async function expandDocument(document, options) {
17992
+ async function expandDocument(document, options, provenance = false) {
17815
17993
  const preserveSet = resolvePreserveSet(options);
17816
17994
  const rootIn = document;
17817
17995
  const internalDocument = rootIn.props === void 0 ? { ...rootIn, props: {} } : rootIn;
@@ -17871,7 +18049,7 @@ function createBuilderImpl(state) {
17871
18049
  warnings,
17872
18050
  resolveNamedTheme: resolveDocumentTheme
17873
18051
  });
17874
- if (modedRoot.props.blocks !== void 0) {
18052
+ if (modedRoot.props.blocks !== void 0 || provenance) {
17875
18053
  const expanded = await expandBlocksWithPlugins(
17876
18054
  modedRoot,
17877
18055
  modedTheme,
@@ -17919,6 +18097,7 @@ function createBuilderImpl(state) {
17919
18097
  modedDoc: modedDoc2,
17920
18098
  sourceMap: expanded.sourceMap,
17921
18099
  blockPaths: expanded.blocks,
18100
+ pluginOutputs: expanded.pluginOutputs,
17922
18101
  warnings,
17923
18102
  preserveSet,
17924
18103
  processed: {
@@ -17967,8 +18146,64 @@ function createBuilderImpl(state) {
17967
18146
  throw error;
17968
18147
  }
17969
18148
  }
18149
+ async function prepareQuality(document, options) {
18150
+ const {
18151
+ modedRoot,
18152
+ modedTheme,
18153
+ themeName,
18154
+ modedDoc,
18155
+ sourceMap,
18156
+ blockPaths,
18157
+ pluginOutputs,
18158
+ warnings
18159
+ } = await expandDocument(document, options, true);
18160
+ const prepared = prepareDocxQualityDocument(
18161
+ document,
18162
+ {
18163
+ context: { document: modedRoot, theme: modedTheme, themeName },
18164
+ expanded: {
18165
+ document: modedDoc,
18166
+ sourceMap: sourceMap ?? {},
18167
+ blocks: blockPaths ?? [],
18168
+ ...pluginOutputs && { pluginOutputs }
18169
+ },
18170
+ customThemes: state.customThemes,
18171
+ fonts: state.fonts,
18172
+ renderer: options?.renderer ?? state.renderer ?? modedRoot.renderer,
18173
+ warnings
18174
+ }
18175
+ );
18176
+ return { prepared, warnings: warnings.length > 0 ? warnings : null };
18177
+ }
18178
+ async function generateBufferFromPrepared(prepared, options) {
18179
+ const { context } = prepared.model;
18180
+ const warnings = [];
18181
+ const baseDir = options?.baseDir ?? state.baseDir;
18182
+ const deterministic = options?.deterministic ?? state.deterministic;
18183
+ const generatedAt = options?.generatedAt ?? state.generatedAt;
18184
+ const renderer = options?.renderer ?? state.renderer ?? prepared.renderer;
18185
+ const { buffer } = await generateBufferViaIr(context.document, {
18186
+ context,
18187
+ prepared,
18188
+ ...state.services ? { services: state.services } : {},
18189
+ ...state.fonts ? { fonts: state.fonts } : {},
18190
+ ...baseDir !== void 0 ? { baseDir } : {},
18191
+ ...deterministic !== void 0 ? { deterministic } : {},
18192
+ ...generatedAt !== void 0 ? { generatedAt } : {},
18193
+ ...renderer !== void 0 ? { renderer } : {},
18194
+ warnings
18195
+ });
18196
+ return {
18197
+ buffer,
18198
+ warnings: warnings.length > 0 ? warnings : null,
18199
+ standardDefinition: context.document,
18200
+ preservedDefinition: void 0
18201
+ };
18202
+ }
17970
18203
  async function generateBuffer(document, options) {
17971
18204
  try {
18205
+ const reused = options?.prepared && resolvePreserveSet(options) === void 0 ? options.prepared : void 0;
18206
+ if (reused) return await generateBufferFromPrepared(reused, options);
17972
18207
  const {
17973
18208
  modedRoot,
17974
18209
  modedTheme,
@@ -18098,6 +18333,7 @@ function createBuilderImpl(state) {
18098
18333
  generateBuffer,
18099
18334
  generateFile,
18100
18335
  expandStandardDefinition,
18336
+ prepareQuality,
18101
18337
  getComponentNames,
18102
18338
  validate,
18103
18339
  generateSchema,