@json-to-office/core-docx 7.0.0 → 7.1.2

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/components/highcharts.d.ts.map +1 -1
  3. package/dist/core/desugarExternals.d.ts.map +1 -1
  4. package/dist/core/imageResources.d.ts.map +1 -1
  5. package/dist/core/layout.d.ts +7 -0
  6. package/dist/core/layout.d.ts.map +1 -1
  7. package/dist/core/tableModel.d.ts +10 -8
  8. package/dist/core/tableModel.d.ts.map +1 -1
  9. package/dist/index.js +455 -219
  10. package/dist/index.js.map +1 -1
  11. package/dist/ir/compiler.d.ts.map +1 -1
  12. package/dist/ir/measure.d.ts +44 -0
  13. package/dist/ir/measure.d.ts.map +1 -0
  14. package/dist/ir/types.d.ts +31 -7
  15. package/dist/ir/types.d.ts.map +1 -1
  16. package/dist/ir/validation.d.ts +3 -2
  17. package/dist/ir/validation.d.ts.map +1 -1
  18. package/dist/plugin/example/index.js +455 -219
  19. package/dist/plugin/example/index.js.map +1 -1
  20. package/dist/quality/facts.d.ts.map +1 -1
  21. package/dist/renderers/docxjs/emit.d.ts.map +1 -1
  22. package/dist/renderers/docxjs/styles.d.ts.map +1 -1
  23. package/dist/renderers/office-open/chartParts.d.ts.map +1 -1
  24. package/dist/renderers/office-open/emit.d.ts +14 -0
  25. package/dist/renderers/office-open/emit.d.ts.map +1 -1
  26. package/dist/renderers/office-open/styles.d.ts.map +1 -1
  27. package/dist/styles/index.d.ts +2 -1
  28. package/dist/styles/index.d.ts.map +1 -1
  29. package/dist/styles/themeToStyles.d.ts +1 -1
  30. package/dist/styles/themeToStyles.d.ts.map +1 -1
  31. package/dist/styles/utils/layoutUtils.d.ts +44 -0
  32. package/dist/styles/utils/layoutUtils.d.ts.map +1 -1
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/dist/utils/widthUtils.d.ts +9 -1
  35. package/dist/utils/widthUtils.d.ts.map +1 -1
  36. package/package.json +3 -3
  37. package/dist/styles/utils/borderUtils.d.ts +0 -95
  38. package/dist/styles/utils/borderUtils.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1847,6 +1847,21 @@ function getPageDimensions(size) {
1847
1847
  }
1848
1848
  return { width: size.width, height: size.height };
1849
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
+ }
1850
1865
  var resolveTableParagraphSpacing, PAGE_SIZES, getTableStyle, getDocumentMargins, getPageSetup;
1851
1866
  var init_layoutUtils = __esm({
1852
1867
  "src/styles/utils/layoutUtils.ts"() {
@@ -1999,6 +2014,7 @@ var init_layoutUtils = __esm({
1999
2014
  if (themeConfig?.page) {
2000
2015
  const margins = themeConfig.page.margins || {};
2001
2016
  const dimensions = getPageDimensions(themeConfig.page.size);
2017
+ const gutter = margins.gutter;
2002
2018
  return {
2003
2019
  size: {
2004
2020
  width: dimensions.width,
@@ -2009,7 +2025,13 @@ var init_layoutUtils = __esm({
2009
2025
  top: margins.top ?? defaultMargins.top ?? 1440,
2010
2026
  right: margins.right ?? defaultMargins.right ?? 1440,
2011
2027
  bottom: margins.bottom ?? defaultMargins.bottom ?? 1440,
2012
- left: margins.left ?? defaultMargins.left ?? 1440
2028
+ left: margins.left ?? defaultMargins.left ?? 1440,
2029
+ // How far the running head and foot sit from the page edge. Left out,
2030
+ // each renderer writes its own default — docx.js 708, office-open 851
2031
+ // and 992 — in place of the distance the theme states.
2032
+ header: margins.header ?? defaultMargins.header ?? 720,
2033
+ footer: margins.footer ?? defaultMargins.footer ?? 720,
2034
+ ...gutter !== void 0 && { gutter }
2013
2035
  }
2014
2036
  };
2015
2037
  }
@@ -2025,50 +2047,15 @@ var init_layoutUtils = __esm({
2025
2047
  top: defaultMargins.top ?? 1440,
2026
2048
  right: defaultMargins.right ?? 1440,
2027
2049
  bottom: defaultMargins.bottom ?? 1440,
2028
- left: defaultMargins.left ?? 1440
2050
+ left: defaultMargins.left ?? 1440,
2051
+ header: defaultMargins.header ?? 720,
2052
+ footer: defaultMargins.footer ?? 720
2029
2053
  }
2030
2054
  };
2031
2055
  };
2032
2056
  }
2033
2057
  });
2034
2058
 
2035
- // src/utils/generationContext.ts
2036
- import { AsyncLocalStorage } from "async_hooks";
2037
- import { isAbsolute, resolve } from "path";
2038
- function runWithBaseDir(baseDir, callback) {
2039
- return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
2040
- }
2041
- function resolveFromBaseDir(filePath) {
2042
- const base = baseDirStorage.getStore();
2043
- if (!base || isAbsolute(filePath)) return filePath;
2044
- return resolve(base, filePath);
2045
- }
2046
- function runWithWarnings(warnings, callback) {
2047
- return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
2048
- }
2049
- function reportWarning(component, code, message, context) {
2050
- const warnings = warningsStorage.getStore();
2051
- if (warnings) {
2052
- warnings.push({
2053
- component,
2054
- message,
2055
- severity: "warning",
2056
- context: { code, ...context }
2057
- });
2058
- return;
2059
- }
2060
- console.warn(`[json-to-docx] [${code}] ${message}`);
2061
- }
2062
- var generationDateStorage, baseDirStorage, warningsStorage;
2063
- var init_generationContext = __esm({
2064
- "src/utils/generationContext.ts"() {
2065
- "use strict";
2066
- generationDateStorage = new AsyncLocalStorage();
2067
- baseDirStorage = new AsyncLocalStorage();
2068
- warningsStorage = new AsyncLocalStorage();
2069
- }
2070
- });
2071
-
2072
2059
  // src/themes/json/parser.ts
2073
2060
  import { validateThemeJson } from "@json-to-office/shared-docx/validation/unified";
2074
2061
  var ThemeValidationError, ThemeParseError, ThemeParser;
@@ -2401,7 +2388,8 @@ function getAvailableWidthTwips(theme, themeName) {
2401
2388
  const width = page?.size?.width || 0;
2402
2389
  const left = page?.margin?.left || 0;
2403
2390
  const right = page?.margin?.right || 0;
2404
- return Math.max(0, width - left - right);
2391
+ const gutter = page?.margin?.gutter || 0;
2392
+ return Math.max(0, width - left - right - gutter);
2405
2393
  }
2406
2394
  function getPageWidthTwips(theme, themeName) {
2407
2395
  const page = getPageSetup(theme, themeName);
@@ -2436,6 +2424,13 @@ function resolveOffsetTwips(value, referenceTwips) {
2436
2424
  }
2437
2425
  return Math.round(referenceTwips * parseFloat(match[1]) / 100);
2438
2426
  }
2427
+ function splitTwips(count, totalTwips) {
2428
+ const edge = (index) => Math.round(totalTwips * index / count);
2429
+ return Array.from(
2430
+ { length: count },
2431
+ (_, index) => edge(index + 1) - edge(index)
2432
+ );
2433
+ }
2439
2434
  function relativeLengthToTwips(value, availableWidthTwips) {
2440
2435
  if (typeof value === "number") {
2441
2436
  return pointsToTwips(value);
@@ -2452,6 +2447,43 @@ var init_widthUtils = __esm({
2452
2447
  }
2453
2448
  });
2454
2449
 
2450
+ // src/utils/generationContext.ts
2451
+ import { AsyncLocalStorage } from "async_hooks";
2452
+ import { isAbsolute, resolve } from "path";
2453
+ function runWithBaseDir(baseDir, callback) {
2454
+ return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
2455
+ }
2456
+ function resolveFromBaseDir(filePath) {
2457
+ const base = baseDirStorage.getStore();
2458
+ if (!base || isAbsolute(filePath)) return filePath;
2459
+ return resolve(base, filePath);
2460
+ }
2461
+ function runWithWarnings(warnings, callback) {
2462
+ return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
2463
+ }
2464
+ function reportWarning(component, code, message, context) {
2465
+ const warnings = warningsStorage.getStore();
2466
+ if (warnings) {
2467
+ warnings.push({
2468
+ component,
2469
+ message,
2470
+ severity: "warning",
2471
+ context: { code, ...context }
2472
+ });
2473
+ return;
2474
+ }
2475
+ console.warn(`[json-to-docx] [${code}] ${message}`);
2476
+ }
2477
+ var generationDateStorage, baseDirStorage, warningsStorage;
2478
+ var init_generationContext = __esm({
2479
+ "src/utils/generationContext.ts"() {
2480
+ "use strict";
2481
+ generationDateStorage = new AsyncLocalStorage();
2482
+ baseDirStorage = new AsyncLocalStorage();
2483
+ warningsStorage = new AsyncLocalStorage();
2484
+ }
2485
+ });
2486
+
2455
2487
  // src/utils/imageUtils.ts
2456
2488
  import { readFileSync } from "fs";
2457
2489
  import { availableParallelism } from "os";
@@ -2747,7 +2779,7 @@ var init_imageUtils = __esm({
2747
2779
  });
2748
2780
 
2749
2781
  // src/ir/types.ts
2750
- var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION;
2782
+ var TWIPS_PER_INCH, TWIPS_PER_POINT2, EMU_PER_INCH, EMU_PER_TWIP, DOCX_IR_SCHEMA_VERSION, DOCX_IR_BORDER_STYLES;
2751
2783
  var init_types = __esm({
2752
2784
  "src/ir/types.ts"() {
2753
2785
  "use strict";
@@ -2756,6 +2788,35 @@ var init_types = __esm({
2756
2788
  EMU_PER_INCH = 914400;
2757
2789
  EMU_PER_TWIP = EMU_PER_INCH / TWIPS_PER_INCH;
2758
2790
  DOCX_IR_SCHEMA_VERSION = 1;
2791
+ DOCX_IR_BORDER_STYLES = [
2792
+ "single",
2793
+ "dashDotStroked",
2794
+ "dashed",
2795
+ "dashSmallGap",
2796
+ "dotDash",
2797
+ "dotDotDash",
2798
+ "dotted",
2799
+ "double",
2800
+ "doubleWave",
2801
+ "inset",
2802
+ "nil",
2803
+ "none",
2804
+ "outset",
2805
+ "thick",
2806
+ "thickThinLargeGap",
2807
+ "thickThinMediumGap",
2808
+ "thickThinSmallGap",
2809
+ "thinThickLargeGap",
2810
+ "thinThickMediumGap",
2811
+ "thinThickSmallGap",
2812
+ "thinThickThinLargeGap",
2813
+ "thinThickThinMediumGap",
2814
+ "thinThickThinSmallGap",
2815
+ "threeDEmboss",
2816
+ "threeDEngrave",
2817
+ "triple",
2818
+ "wave"
2819
+ ];
2759
2820
  }
2760
2821
  });
2761
2822
 
@@ -2815,7 +2876,6 @@ import {
2815
2876
  AlignmentType,
2816
2877
  BookmarkEnd,
2817
2878
  BookmarkStart,
2818
- BorderStyle,
2819
2879
  ColumnBreak,
2820
2880
  CommentRangeEnd,
2821
2881
  CommentRangeStart,
@@ -3232,7 +3292,7 @@ function emitBlock(block2, resources = /* @__PURE__ */ new Map()) {
3232
3292
  }
3233
3293
  }
3234
3294
  function emitToc(block2) {
3235
- return new TableOfContents(block2.alias ?? "Table of Contents", {
3295
+ return new ThemedTableOfContents(block2.alias ?? "Table of Contents", {
3236
3296
  ...block2.hyperlink !== void 0 ? { hyperlink: block2.hyperlink } : {},
3237
3297
  ...block2.headingRange ? {
3238
3298
  headingStyleRange: `${block2.headingRange.from}-${block2.headingRange.to}`
@@ -3261,7 +3321,8 @@ function emitTable(block2, resources = /* @__PURE__ */ new Map()) {
3261
3321
  },
3262
3322
  layout: block2.layout === "fixed" ? TableLayoutType.FIXED : TableLayoutType.AUTOFIT,
3263
3323
  // An empty grid is a table with nothing to say about its columns, which is
3264
- // not the same as one whose columns are all zero wide.
3324
+ // not the same as one whose columns are all zero wide. The compiler hands
3325
+ // over every grid in twips, the unit `columnWidths` takes.
3265
3326
  ...block2.columnGrid.values.length > 0 ? { columnWidths: block2.columnGrid.values } : {},
3266
3327
  ...block2.borders ? {
3267
3328
  borders: {
@@ -3355,7 +3416,10 @@ function emitTableCell(cell, resources) {
3355
3416
  function emitBorder(border3) {
3356
3417
  if (!border3) return void 0;
3357
3418
  return {
3358
- style: BORDER_STYLE[border3.style] ?? BorderStyle.SINGLE,
3419
+ // Already OOXML's word, and docx.js's `BorderStyle` values are exactly
3420
+ // those words. Mapping unknown ones to `single` here is what once hid an
3421
+ // authoring word the IR should never have carried.
3422
+ style: border3.style,
3359
3423
  size: border3.sizeEighthPoints ?? 0,
3360
3424
  color: border3.color?.hex ?? "000000",
3361
3425
  // Stated only when the IR states it, so every border that predates the
@@ -3364,7 +3428,7 @@ function emitBorder(border3) {
3364
3428
  ...border3.spacePoints !== void 0 ? { space: border3.spacePoints } : {}
3365
3429
  };
3366
3430
  }
3367
- var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, VERTICAL_ALIGN, TABLE_ANCHOR, HORIZONTAL_POSITION, VERTICAL_POSITION, BORDER_STYLE;
3431
+ var ALIGNMENT2, PAGE_FIELDS, isSequenceField, WRAP_TYPE, ThemedTableOfContents, VERTICAL_ALIGN, TABLE_ANCHOR, HORIZONTAL_POSITION, VERTICAL_POSITION;
3368
3432
  var init_emit = __esm({
3369
3433
  "src/renderers/docxjs/emit.ts"() {
3370
3434
  "use strict";
@@ -3384,6 +3448,11 @@ var init_emit = __esm({
3384
3448
  tight: TextWrappingType.TIGHT,
3385
3449
  topAndBottom: TextWrappingType.TOP_AND_BOTTOM
3386
3450
  };
3451
+ ThemedTableOfContents = class extends TableOfContents {
3452
+ };
3453
+ Object.defineProperty(ThemedTableOfContents.prototype, "getTabStopsForLevel", {
3454
+ value: () => []
3455
+ });
3387
3456
  VERTICAL_ALIGN = {
3388
3457
  top: VerticalAlign.TOP,
3389
3458
  center: VerticalAlign.CENTER,
@@ -3408,20 +3477,10 @@ var init_emit = __esm({
3408
3477
  inside: RelativeVerticalPosition.INSIDE,
3409
3478
  outside: RelativeVerticalPosition.OUTSIDE
3410
3479
  };
3411
- BORDER_STYLE = {
3412
- none: BorderStyle.NONE,
3413
- single: BorderStyle.SINGLE,
3414
- double: BorderStyle.DOUBLE,
3415
- dashed: BorderStyle.DASHED,
3416
- dotted: BorderStyle.DOTTED
3417
- };
3418
3480
  }
3419
3481
  });
3420
3482
 
3421
3483
  // src/renderers/docxjs/styles.ts
3422
- import {
3423
- BorderStyle as BorderStyle2
3424
- } from "docx";
3425
3484
  function runProperties(formatting) {
3426
3485
  const out = {};
3427
3486
  if (formatting.fontFamily !== void 0) out.font = formatting.fontFamily;
@@ -3430,7 +3489,7 @@ function runProperties(formatting) {
3430
3489
  }
3431
3490
  if (formatting.color) out.color = formatting.color.hex;
3432
3491
  if (formatting.bold !== void 0) out.bold = formatting.bold;
3433
- if (formatting.italic !== void 0) out.italic = formatting.italic;
3492
+ if (formatting.italic !== void 0) out.italics = formatting.italic;
3434
3493
  if (formatting.underline) {
3435
3494
  out.underline = {
3436
3495
  type: formatting.underline.type,
@@ -3442,7 +3501,9 @@ function runProperties(formatting) {
3442
3501
  if (formatting.subScript) out.subScript = true;
3443
3502
  if (formatting.smallCaps !== void 0) out.smallCaps = formatting.smallCaps;
3444
3503
  if (formatting.allCaps !== void 0) out.allCaps = formatting.allCaps;
3445
- if (formatting.highlight !== void 0) out.highlight = formatting.highlight;
3504
+ if (formatting.highlight !== void 0) {
3505
+ out.highlight = formatting.highlight;
3506
+ }
3446
3507
  if (formatting.characterSpacingTwentieths !== void 0) {
3447
3508
  out.characterSpacing = formatting.characterSpacingTwentieths;
3448
3509
  }
@@ -3456,7 +3517,10 @@ function runProperties(formatting) {
3456
3517
  }
3457
3518
  function border(value) {
3458
3519
  return {
3459
- style: BORDER_STYLE2[value.style] ?? BorderStyle2.SINGLE,
3520
+ // OOXML's word, which is what docx.js's `BorderStyle` values are. A theme
3521
+ // may state any of them; mapping the unlisted ones to `single` drew a
3522
+ // `wave` or `triple` rule as a plain line here and as asked on office-open.
3523
+ style: value.style,
3460
3524
  ...value.sizeEighthPoints !== void 0 ? { size: value.sizeEighthPoints } : {},
3461
3525
  ...value.color ? { color: value.color.hex } : {},
3462
3526
  ...value.spacePoints !== void 0 ? { space: value.spacePoints } : {}
@@ -3569,19 +3633,10 @@ function emitStyles(styles) {
3569
3633
  }
3570
3634
  };
3571
3635
  }
3572
- var BORDER_STYLE2;
3573
3636
  var init_styles2 = __esm({
3574
3637
  "src/renderers/docxjs/styles.ts"() {
3575
3638
  "use strict";
3576
3639
  init_emit();
3577
- BORDER_STYLE2 = {
3578
- none: BorderStyle2.NONE,
3579
- single: BorderStyle2.SINGLE,
3580
- double: BorderStyle2.DOUBLE,
3581
- dashed: BorderStyle2.DASHED,
3582
- dotted: BorderStyle2.DOTTED,
3583
- thick: BorderStyle2.THICK
3584
- };
3585
3640
  }
3586
3641
  });
3587
3642
 
@@ -3760,7 +3815,7 @@ import {
3760
3815
  Header,
3761
3816
  ImageRun,
3762
3817
  Packer,
3763
- LineRuleType as LineRuleType2,
3818
+ LineRuleType,
3764
3819
  Paragraph as Paragraph2
3765
3820
  } from "docx";
3766
3821
  function createDocxJsRenderer() {
@@ -4047,7 +4102,7 @@ function lastBlockIsFrame(section2) {
4047
4102
  function anchorParagraph(anchor) {
4048
4103
  return new Paragraph2({
4049
4104
  children: anchor ? [anchor] : [],
4050
- spacing: { before: 0, after: 0, line: 20, lineRule: LineRuleType2.EXACT }
4105
+ spacing: { before: 0, after: 0, line: 20, lineRule: LineRuleType.EXACT }
4051
4106
  });
4052
4107
  }
4053
4108
  function coreProperties(ir) {
@@ -4144,13 +4199,30 @@ import {
4144
4199
  spliceChartXml
4145
4200
  } from "@json-to-office/shared/rendering";
4146
4201
  function spliceInput(chart) {
4202
+ const { bold, ...rest } = chart.textFont;
4203
+ const textFont = rest;
4204
+ const axisTitleFont = { ...rest, bold };
4205
+ const chartTitleFont = {
4206
+ ...rest,
4207
+ fontSize: Math.round(rest.fontSize * CHART_TITLE_SCALE),
4208
+ bold: true
4209
+ };
4147
4210
  return {
4148
4211
  chartType: chart.chartType,
4149
4212
  series: chart.series,
4150
4213
  colors: chart.colors,
4214
+ textFont,
4215
+ titleFont: chartTitleFont,
4151
4216
  ...chart.legendPosition ? { legendPosition: chart.legendPosition } : {},
4152
- ...chart.categoryAxisTitle ? { categoryAxis: { title: chart.categoryAxisTitle } } : {},
4153
- ...chart.valueAxisTitle ? { valueAxis: { title: chart.valueAxisTitle } } : {}
4217
+ ...chart.categoryAxisTitle ? {
4218
+ categoryAxis: {
4219
+ title: chart.categoryAxisTitle,
4220
+ titleFont: axisTitleFont
4221
+ }
4222
+ } : {},
4223
+ ...chart.valueAxisTitle ? {
4224
+ valueAxis: { title: chart.valueAxisTitle, titleFont: axisTitleFont }
4225
+ } : {}
4154
4226
  };
4155
4227
  }
4156
4228
  function declareWorkbookContentType(zip) {
@@ -4194,10 +4266,12 @@ function spliceChartParts(zip, charts) {
4194
4266
  }
4195
4267
  declareWorkbookContentType(zip);
4196
4268
  }
4269
+ var CHART_TITLE_SCALE;
4197
4270
  var init_chartParts = __esm({
4198
4271
  "src/renderers/office-open/chartParts.ts"() {
4199
4272
  "use strict";
4200
4273
  init_chartWorkbook();
4274
+ CHART_TITLE_SCALE = 1.4;
4201
4275
  }
4202
4276
  });
4203
4277
 
@@ -4221,10 +4295,17 @@ function runProperties2(formatting) {
4221
4295
  if (formatting.fontFamily) out.font = formatting.fontFamily;
4222
4296
  if (formatting.sizeHalfPoints !== void 0) {
4223
4297
  out.size = formatting.sizeHalfPoints / 2;
4298
+ if (formatting.sizeHalfPoints) out.sizeComplexScript = out.size;
4224
4299
  }
4225
4300
  if (formatting.color) out.color = formatting.color.hex;
4226
- if (formatting.bold !== void 0) out.bold = formatting.bold;
4227
- if (formatting.italic !== void 0) out.italic = formatting.italic;
4301
+ if (formatting.bold !== void 0) {
4302
+ out.bold = formatting.bold;
4303
+ out.boldComplexScript = formatting.bold;
4304
+ }
4305
+ if (formatting.italic !== void 0) {
4306
+ out.italic = formatting.italic;
4307
+ out.italicComplexScript = formatting.italic;
4308
+ }
4228
4309
  if (formatting.underline) {
4229
4310
  out.underline = {
4230
4311
  type: formatting.underline.type,
@@ -4743,9 +4824,6 @@ function tocEntry(entry) {
4743
4824
  return {
4744
4825
  paragraph: {
4745
4826
  style: `TOC${entry.level}`,
4746
- tabStops: [
4747
- { type: "right", position: TOC_PAGE_TAB_TWIPS, leader: "dot" }
4748
- ],
4749
4827
  children: [{ text: entry.text }, { children: [{ tab: true }] }]
4750
4828
  }
4751
4829
  };
@@ -4775,13 +4853,17 @@ function table(value, ctx) {
4775
4853
  return {
4776
4854
  rows: value.rows.map((row) => tableRow(row, ctx)),
4777
4855
  width: {
4778
- size: value.width.kind === "auto" ? 0 : value.width.value,
4856
+ // `w:w` is an integer in OOXML. A grid that splits a remainder between
4857
+ // unstated columns comes out fractional, and the XML would carry it
4858
+ // verbatim; floored here, as the docxjs backend floors it.
4859
+ size: value.width.kind === "auto" ? 0 : value.width.kind === "twips" ? Math.floor(value.width.value) : value.width.value,
4779
4860
  type: value.width.kind === "twips" ? "dxa" : value.width.kind === "percent" ? "pct" : "auto"
4780
4861
  },
4781
4862
  layout: value.layout,
4782
4863
  // An empty grid is a table with nothing to say about its columns, which is
4783
- // not the same as one whose columns are all zero wide.
4784
- ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values } : {},
4864
+ // not the same as one whose columns are all zero wide. The compiler hands
4865
+ // over every grid in twips; floored for the same reason as the width.
4866
+ ...value.columnGrid.values.length > 0 ? { columnWidths: value.columnGrid.values.map(Math.floor) } : {},
4785
4867
  ...value.alignment ? { alignment: alignment(value.alignment) } : {},
4786
4868
  ...value.borders ? { borders: borders2(value.borders) } : {},
4787
4869
  ...value.cellMargins ? { margins: cellMargins(value.cellMargins) } : {},
@@ -4915,7 +4997,13 @@ function section(value, ctx, closesDocument = false) {
4915
4997
  }))
4916
4998
  } : {}
4917
4999
  }
4918
- } : {}
5000
+ } : {},
5001
+ // No document grid, as the IR states none. Left to the backend, every
5002
+ // section gets the one Word's Chinese template sets — a 15.6pt line grid
5003
+ // (`w:type="lines"`, pitch 312) — and every line is laid out on it here
5004
+ // but at its own height on docx.js, which writes no grid type. A field
5005
+ // the backend is not given takes that default, so all three are stated.
5006
+ grid: { type: "default", linePitch: 360, charSpace: 0 }
4919
5007
  },
4920
5008
  children: sectionChildren2(value, ctx, closesDocument),
4921
5009
  ...value.headers ? { headers: headerFooterSet(value.headers, ctx) } : {},
@@ -4973,7 +5061,7 @@ function numberingConfig2(numbering) {
4973
5061
  }))
4974
5062
  };
4975
5063
  }
4976
- var WRAP_TYPE2, BODY_ANCHOR, TOC_PAGE_TAB_TWIPS;
5064
+ var WRAP_TYPE2, BODY_ANCHOR;
4977
5065
  var init_emit2 = __esm({
4978
5066
  "src/renderers/office-open/emit.ts"() {
4979
5067
  "use strict";
@@ -4989,7 +5077,6 @@ var init_emit2 = __esm({
4989
5077
  middle: "ctr",
4990
5078
  bottom: "b"
4991
5079
  };
4992
- TOC_PAGE_TAB_TWIPS = 9025;
4993
5080
  }
4994
5081
  });
4995
5082
 
@@ -5001,12 +5088,11 @@ function builtIn2(style) {
5001
5088
  };
5002
5089
  }
5003
5090
  function emitStyles2(styles) {
5004
- const documentDefaults = {
5005
- ...Object.keys(styles.defaults.run).length > 0 ? { run: runProperties2(styles.defaults.run) } : {},
5006
- ...Object.keys(styles.defaults.paragraph).length > 0 ? { paragraph: paragraphProperties2(styles.defaults.paragraph) } : {}
5007
- };
5008
5091
  const defaults = {
5009
- ...Object.keys(documentDefaults).length > 0 ? { document: documentDefaults } : {}
5092
+ document: {
5093
+ run: Object.keys(styles.defaults.run).length > 0 ? runProperties2(styles.defaults.run) : null,
5094
+ paragraph: Object.keys(styles.defaults.paragraph).length > 0 ? paragraphProperties2(styles.defaults.paragraph) : null
5095
+ }
5010
5096
  };
5011
5097
  for (const slot of [
5012
5098
  "footnoteText",
@@ -5035,7 +5121,7 @@ function emitStyles2(styles) {
5035
5121
  run: runProperties2(style.run)
5036
5122
  }))
5037
5123
  } : {},
5038
- ...Object.keys(defaults).length > 0 ? { default: defaults } : {}
5124
+ default: defaults
5039
5125
  };
5040
5126
  }
5041
5127
  var init_styles3 = __esm({
@@ -5270,7 +5356,9 @@ function addPngTextChunk(data, marker) {
5270
5356
  }
5271
5357
  offset = chunkEnd;
5272
5358
  }
5273
- return Buffer.concat([data, marker]);
5359
+ throw new Error(
5360
+ "Cannot mark a PNG that has no intact IEND chunk; its bytes were not checked to decode before rendering"
5361
+ );
5274
5362
  }
5275
5363
  function addJpegComment(data, marker) {
5276
5364
  if (data.length < 2 || data[0] !== 255 || data[1] !== 216) {
@@ -5969,6 +6057,7 @@ function containsComponent(root, name) {
5969
6057
  }
5970
6058
 
5971
6059
  // src/core/desugarExternals.ts
6060
+ init_layoutUtils();
5972
6061
  import {
5973
6062
  clampVisualDpi as clampVisualDpi2,
5974
6063
  DEFAULT_VISUAL_DPI as DEFAULT_VISUAL_DPI2,
@@ -5980,7 +6069,7 @@ import {
5980
6069
 
5981
6070
  // src/components/highcharts.ts
5982
6071
  init_colorUtils();
5983
- init_layoutUtils();
6072
+ init_widthUtils();
5984
6073
  import {
5985
6074
  chartFamilyResolver,
5986
6075
  remoteExportNotice,
@@ -6091,8 +6180,7 @@ function placedWidthPt(config, theme) {
6091
6180
  const chart = config.options.chart;
6092
6181
  if (typeof width === "number") return width * POINTS_PER_PIXEL;
6093
6182
  if (typeof width === "string") {
6094
- const page = getPageSetup(theme);
6095
- const contentWidthPt = (page.size.width - page.margin.left - page.margin.right) * POINTS_PER_TWIP;
6183
+ const contentWidthPt = getAvailableWidthTwips(theme) * POINTS_PER_TWIP;
6096
6184
  return parseFloat(width) / 100 * contentWidthPt;
6097
6185
  }
6098
6186
  if (typeof height === "number" && chart.height > 0) {
@@ -6172,8 +6260,18 @@ async function desugarExternals(document, options) {
6172
6260
  }
6173
6261
  ).catch(() => /* @__PURE__ */ new Map());
6174
6262
  const chartCache = /* @__PURE__ */ new Map();
6175
- return transformComponents(document, async (node) => {
6263
+ const replace = (theme, inSection) => async function replaceNode(node) {
6176
6264
  if (node.enabled === false) return void 0;
6265
+ if (node.name === "section" && !inSection) {
6266
+ const page = node.props.page;
6267
+ return transformComponents(
6268
+ node,
6269
+ replace(themeForSectionPage(theme, page), true)
6270
+ );
6271
+ }
6272
+ return replaceExternal(node, theme);
6273
+ };
6274
+ const replaceExternal = async (node, theme) => {
6177
6275
  if (node.name === "visual") {
6178
6276
  if (isNativeVisualProps2(node.props)) return void 0;
6179
6277
  return withNodeIdentity(node, {
@@ -6191,7 +6289,7 @@ async function desugarExternals(document, options) {
6191
6289
  name: "image",
6192
6290
  props: await renderChartToImageProps(
6193
6291
  node.props,
6194
- options.theme,
6292
+ theme,
6195
6293
  options.services?.highcharts,
6196
6294
  options.chartFonts,
6197
6295
  options.warnings,
@@ -6200,7 +6298,8 @@ async function desugarExternals(document, options) {
6200
6298
  });
6201
6299
  }
6202
6300
  return void 0;
6203
- });
6301
+ };
6302
+ return transformComponents(document, replace(options.theme, false));
6204
6303
  }
6205
6304
  async function visualImageProps(props, prerastered, options) {
6206
6305
  const serviceConfig = options.services?.pptx;
@@ -6235,6 +6334,7 @@ init_imageUtils();
6235
6334
  import {
6236
6335
  isNativeVisualProps as isNativeVisualProps3
6237
6336
  } from "@json-to-office/shared-docx";
6337
+ import { imageIntegrityDefect } from "@json-to-office/shared/images/node";
6238
6338
  async function loadImageResources(components) {
6239
6339
  const sources = /* @__PURE__ */ new Set();
6240
6340
  for (const component of components) collectSources(component, sources);
@@ -6244,6 +6344,8 @@ async function loadImageResources(components) {
6244
6344
  [...sources].map(async (source) => {
6245
6345
  try {
6246
6346
  const result = await getImageBuffer(source);
6347
+ const defect = imageIntegrityDefect(result.buffer);
6348
+ if (defect) throw new Error(defect);
6247
6349
  const intrinsic = readIntrinsic(result.buffer, source);
6248
6350
  loaded.set(source, {
6249
6351
  bytes: result.buffer,
@@ -6842,6 +6944,7 @@ function resolveColors(entries, at, deps) {
6842
6944
 
6843
6945
  // src/styles/themeToStyles.ts
6844
6946
  init_themes();
6947
+ init_types();
6845
6948
  init_colorUtils();
6846
6949
  init_styleHelpers();
6847
6950
  import {
@@ -7008,10 +7111,13 @@ function convertParagraphProperties(merged, styleProps, theme) {
7008
7111
  }
7009
7112
  };
7010
7113
  }
7114
+ function themeBorderStyle(style) {
7115
+ return DOCX_IR_BORDER_STYLES.includes(style) ? style : "single";
7116
+ }
7011
7117
  function convertBorders(borders3, theme) {
7012
7118
  if (!borders3) return void 0;
7013
7119
  const mapSide = (side) => side ? {
7014
- style: side.style,
7120
+ style: themeBorderStyle(side.style),
7015
7121
  sizeEighthPoints: side.size,
7016
7122
  color: { hex: resolveColor(side.color, theme) },
7017
7123
  ...side.space !== void 0 ? { spacePoints: side.space } : {}
@@ -8198,7 +8304,11 @@ function resolveTableModel(source, theme, themeName, options = {}) {
8198
8304
  });
8199
8305
  adjudicateInteriorEdges([...hasHeader ? [header] : [], ...rows]);
8200
8306
  return {
8201
- ...resolveWidths(source, columns, theme, themeName),
8307
+ ...resolveWidths(
8308
+ source,
8309
+ columns,
8310
+ options.measureTwips ?? getAvailableWidthTwips(theme, themeName)
8311
+ ),
8202
8312
  ...hasHeader ? { header } : {},
8203
8313
  rows,
8204
8314
  repeatHeader: source.repeatHeaderOnPageBreak ?? true
@@ -8252,22 +8362,24 @@ function adjudicateInteriorEdges(rows) {
8252
8362
  });
8253
8363
  });
8254
8364
  }
8255
- function resolveWidths(source, columns, theme, themeName) {
8365
+ function resolveWidths(source, columns, measureTwips) {
8256
8366
  const hasExplicitWidths = columns.some((col) => col.width !== void 0);
8257
8367
  if (!hasExplicitWidths) {
8258
- const share = 100 / columns.length;
8368
+ const percent = source.width ?? 100;
8259
8369
  return {
8260
- columnGrid: { unit: "percent", values: columns.map(() => share) },
8261
- width: { size: source.width ?? 100, unit: "percent" }
8370
+ columnGrid: {
8371
+ unit: "twips",
8372
+ values: splitTwips(columns.length, measureTwips * percent / 100)
8373
+ },
8374
+ width: { size: percent, unit: "percent" }
8262
8375
  };
8263
8376
  }
8264
- const available = getAvailableWidthTwips(theme, themeName);
8265
8377
  const stated = columns.map(
8266
- (col) => col.width !== void 0 ? relativeLengthToTwips(col.width, available) : void 0
8378
+ (col) => col.width !== void 0 ? relativeLengthToTwips(col.width, measureTwips) : void 0
8267
8379
  );
8268
8380
  const totalStated = stated.reduce((sum, w) => sum + (w ?? 0), 0);
8269
8381
  const unstatedCount = columns.filter((col) => col.width === void 0).length;
8270
- const remaining = Math.max(0, available - totalStated);
8382
+ const remaining = Math.max(0, measureTwips - totalStated);
8271
8383
  const shareOfRemainder = unstatedCount > 0 ? remaining / unstatedCount : pointsToTwips(72);
8272
8384
  const values = stated.map((w) => w || shareOfRemainder);
8273
8385
  return {
@@ -8276,7 +8388,7 @@ function resolveWidths(source, columns, theme, themeName) {
8276
8388
  size: values.reduce((sum, w) => sum + w, 0),
8277
8389
  unit: "twips"
8278
8390
  },
8279
- ...totalStated > available ? { overflow: { totalTwips: totalStated, availableTwips: available } } : {}
8391
+ ...totalStated > measureTwips ? { overflow: { totalTwips: totalStated, availableTwips: measureTwips } } : {}
8280
8392
  };
8281
8393
  }
8282
8394
 
@@ -8992,6 +9104,36 @@ function emptyToUndefined(formatting) {
8992
9104
 
8993
9105
  // src/ir/compiler.ts
8994
9106
  init_types();
9107
+
9108
+ // src/ir/measure.ts
9109
+ var DEFAULT_COLUMN_SPACE_TWIPS = 720;
9110
+ function textWidthTwips(page) {
9111
+ const { leftTwips, rightTwips, gutterTwips = 0 } = page.margins;
9112
+ return Math.max(0, page.widthTwips - leftTwips - rightTwips - gutterTwips);
9113
+ }
9114
+ function sectionMeasureTwips(properties) {
9115
+ const textWidth = textWidthTwips(properties.page);
9116
+ const columns = properties.columns;
9117
+ if (!columns || columns.count <= 1) return textWidth;
9118
+ if (columns.equalWidth !== true && columns.widths?.length) {
9119
+ return Math.min(...columns.widths.map((column) => column.widthTwips));
9120
+ }
9121
+ const space = columns.spaceTwips ?? DEFAULT_COLUMN_SPACE_TWIPS;
9122
+ return Math.max(0, (textWidth - space * (columns.count - 1)) / columns.count);
9123
+ }
9124
+ function cellMeasureTwips(widthTwips, margins) {
9125
+ return Math.max(
9126
+ 0,
9127
+ widthTwips - (margins?.leftTwips ?? 0) - (margins?.rightTwips ?? 0)
9128
+ );
9129
+ }
9130
+ function tableWidthTwips(width, measureTwips) {
9131
+ if (width.kind === "twips") return width.value;
9132
+ if (width.kind === "percent") return measureTwips * width.value / 100;
9133
+ return measureTwips;
9134
+ }
9135
+
9136
+ // src/ir/compiler.ts
8995
9137
  init_units();
8996
9138
  var CONTENT_BOOKMARK_ID_BASE = 2e6;
8997
9139
  var SINGLE_LINE_TWIPS = 240;
@@ -9170,11 +9312,64 @@ function resolveSectionChrome(sections) {
9170
9312
  });
9171
9313
  }
9172
9314
  function compileSection(section2, index, ordinal, chrome, ctx) {
9315
+ const documentTheme = ctx.theme;
9316
+ ctx.theme = themeForSectionPage(documentTheme, section2.pageOverride);
9317
+ try {
9318
+ return compileSectionBody(section2, index, ordinal, chrome, ctx);
9319
+ } finally {
9320
+ ctx.theme = documentTheme;
9321
+ }
9322
+ }
9323
+ function compileSectionBody(section2, index, ordinal, chrome, ctx) {
9173
9324
  const path4 = `sections[${index}]`;
9174
9325
  const { page, column } = section2.properties;
9326
+ if (section2.forcedPageBreak) {
9327
+ warnOnce(
9328
+ ctx,
9329
+ "section",
9330
+ `${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.`,
9331
+ "W_SECTION_PAGE_BREAK_FORCED"
9332
+ );
9333
+ }
9175
9334
  if (column && column.count > 1) {
9176
9335
  ctx.features.require("columns", `${path4}.properties.columns`);
9177
9336
  }
9337
+ const properties = {
9338
+ page: {
9339
+ widthTwips: page.size.width,
9340
+ heightTwips: page.size.height,
9341
+ orientation: page.size.width > page.size.height ? "landscape" : "portrait",
9342
+ ...page.size.code !== void 0 ? { code: page.size.code } : {},
9343
+ margins: {
9344
+ topTwips: page.margin.top,
9345
+ bottomTwips: page.margin.bottom,
9346
+ leftTwips: page.margin.left,
9347
+ rightTwips: page.margin.right,
9348
+ ...page.margin.header !== void 0 ? { headerTwips: page.margin.header } : {},
9349
+ ...page.margin.footer !== void 0 ? { footerTwips: page.margin.footer } : {},
9350
+ ...page.margin.gutter !== void 0 ? { gutterTwips: page.margin.gutter } : {}
9351
+ }
9352
+ },
9353
+ // A chunk that starts on a new page says so; the rest continue the flow.
9354
+ // The layout stage already decided which, so it is carried rather than
9355
+ // re-derived from `breakBefore`: a section can be continuous without the
9356
+ // two agreeing.
9357
+ ...section2.properties.type ? { type: sectionType(section2.properties.type) } : {},
9358
+ ...column ? {
9359
+ columns: {
9360
+ count: column.count,
9361
+ ...column.space !== void 0 ? { spaceTwips: column.space } : {},
9362
+ ...column.equalWidth !== void 0 ? { equalWidth: column.equalWidth } : {},
9363
+ ...column.widths ? {
9364
+ widths: column.widths.map((c) => ({
9365
+ widthTwips: c.width,
9366
+ ...c.space !== void 0 ? { spaceTwips: c.space } : {}
9367
+ }))
9368
+ } : {}
9369
+ }
9370
+ } : {}
9371
+ };
9372
+ const measureTwips = sectionMeasureTwips(properties);
9178
9373
  const children = [];
9179
9374
  const sectionBookmarkName = section2.belongsToUserSection && ordinal.ordinal !== void 0 ? `_Section_${ordinal.ordinal}` : void 0;
9180
9375
  section2.components.forEach((component, i) => {
@@ -9184,6 +9379,7 @@ function compileSection(section2, index, ordinal, chrome, ctx) {
9184
9379
  ctx,
9185
9380
  path: `${path4}.children[${i}]`,
9186
9381
  id: blockId(index, [i]),
9382
+ measureTwips,
9187
9383
  ...sectionBookmarkName ? { sectionBookmarkName } : {}
9188
9384
  })
9189
9385
  );
@@ -9196,41 +9392,7 @@ function compileSection(section2, index, ordinal, chrome, ctx) {
9196
9392
  id: `s${index}`,
9197
9393
  path: path4,
9198
9394
  children,
9199
- properties: {
9200
- page: {
9201
- widthTwips: page.size.width,
9202
- heightTwips: page.size.height,
9203
- orientation: page.size.width > page.size.height ? "landscape" : "portrait",
9204
- ...page.size.code !== void 0 ? { code: page.size.code } : {},
9205
- margins: {
9206
- topTwips: page.margin.top,
9207
- bottomTwips: page.margin.bottom,
9208
- leftTwips: page.margin.left,
9209
- rightTwips: page.margin.right,
9210
- ...page.margin.header !== void 0 ? { headerTwips: page.margin.header } : {},
9211
- ...page.margin.footer !== void 0 ? { footerTwips: page.margin.footer } : {},
9212
- ...page.margin.gutter !== void 0 ? { gutterTwips: page.margin.gutter } : {}
9213
- }
9214
- },
9215
- // A chunk that starts on a new page says so; the rest continue the flow.
9216
- // The layout stage already decided which, so it is carried rather than
9217
- // re-derived from `breakBefore`: a section can be continuous without the
9218
- // two agreeing.
9219
- ...section2.properties.type ? { type: sectionType(section2.properties.type) } : {},
9220
- ...column ? {
9221
- columns: {
9222
- count: column.count,
9223
- ...column.space !== void 0 ? { spaceTwips: column.space } : {},
9224
- ...column.equalWidth !== void 0 ? { equalWidth: column.equalWidth } : {},
9225
- ...column.widths ? {
9226
- widths: column.widths.map((c) => ({
9227
- widthTwips: c.width,
9228
- ...c.space !== void 0 ? { spaceTwips: c.space } : {}
9229
- }))
9230
- } : {}
9231
- }
9232
- } : {}
9233
- }
9395
+ properties
9234
9396
  };
9235
9397
  if (ordinal.ordinal !== void 0) {
9236
9398
  compiled.bookmark = {
@@ -9241,9 +9403,22 @@ function compileSection(section2, index, ordinal, chrome, ctx) {
9241
9403
  };
9242
9404
  ctx.features.require("bookmarks", `${path4}.bookmark`);
9243
9405
  }
9244
- const headers = compilePart(chrome.header, "header", index, ctx);
9406
+ const chromeMeasureTwips = textWidthTwips(properties.page);
9407
+ const headers = compilePart(
9408
+ chrome.header,
9409
+ "header",
9410
+ index,
9411
+ chromeMeasureTwips,
9412
+ ctx
9413
+ );
9245
9414
  if (headers) compiled.headers = { default: headers };
9246
- const footers = compilePart(chrome.footer, "footer", index, ctx);
9415
+ const footers = compilePart(
9416
+ chrome.footer,
9417
+ "footer",
9418
+ index,
9419
+ chromeMeasureTwips,
9420
+ ctx
9421
+ );
9247
9422
  if (footers) compiled.footers = { default: footers };
9248
9423
  if (headers || footers) {
9249
9424
  ctx.features.require("headers-footers", `${path4}.headers`);
@@ -9262,7 +9437,7 @@ function sectionType(value) {
9262
9437
  return void 0;
9263
9438
  }
9264
9439
  }
9265
- function compilePart(part, kind, sectionIndex, ctx) {
9440
+ function compilePart(part, kind, sectionIndex, measureTwips, ctx) {
9266
9441
  if (!part) return void 0;
9267
9442
  const id = `${kind}:s${sectionIndex}:default`;
9268
9443
  const children = [];
@@ -9273,7 +9448,8 @@ function compilePart(part, kind, sectionIndex, ctx) {
9273
9448
  const scope = {
9274
9449
  ctx,
9275
9450
  path: `sections[${sectionIndex}].${kind}s.default.children[${i}]`,
9276
- id: headerFooterBlockId(id, [i])
9451
+ id: headerFooterBlockId(id, [i]),
9452
+ measureTwips
9277
9453
  };
9278
9454
  children.push(
9279
9455
  ...component.name === "paragraph" ? compileChromeParagraph(component, scope) : component.name === "image" ? compileChromeImage(component, scope) : compileComponent(component, scope)
@@ -9349,9 +9525,7 @@ function compileChromeImage(component, scope) {
9349
9525
  if (mediaType === "svg") ctx.features.require("svg-images", path4);
9350
9526
  const page = getPageSetup(ctx.theme);
9351
9527
  const pageWidthPx = Math.round(twipsToPixels(page.size.width));
9352
- const contentWidthPx = Math.round(
9353
- twipsToPixels(page.size.width - page.margin.left - page.margin.right)
9354
- );
9528
+ const contentWidthPx = Math.round(twipsToPixels(scope.measureTwips));
9355
9529
  const pageHeightPx = Math.round(twipsToPixels(page.size.height));
9356
9530
  const contentHeightPx = Math.round(
9357
9531
  twipsToPixels(page.size.height - page.margin.top - page.margin.bottom)
@@ -9559,7 +9733,7 @@ function compileColumns(component, scope) {
9559
9733
  const props = component.props ?? {};
9560
9734
  const configs = columnConfigs(props.columns);
9561
9735
  if (configs.length === 0) return [];
9562
- const available = getAvailableWidthTwips(ctx.theme, ctx.themeName);
9736
+ const available = scope.measureTwips;
9563
9737
  const { widths, gaps } = columnMetrics(configs, props.gap, available);
9564
9738
  const stated = widths.map(
9565
9739
  (width, index) => width + gaps[index] / 2 + (index > 0 ? gaps[index - 1] / 2 : 0)
@@ -9588,11 +9762,18 @@ function compileColumns(component, scope) {
9588
9762
  rows: [
9589
9763
  {
9590
9764
  cells: configs.map((_, index) => {
9765
+ const margins = {
9766
+ topTwips: 0,
9767
+ rightTwips: gaps[index] / 2,
9768
+ bottomTwips: 0,
9769
+ leftTwips: index > 0 ? gaps[index - 1] / 2 : 0
9770
+ };
9591
9771
  const blocks = contents[index].flatMap(
9592
9772
  (child, childIndex) => compileComponent(child, {
9593
9773
  ...scope,
9594
9774
  path: `${path4}.children[${index}][${childIndex}]`,
9595
- id: `${scope.id}:c${index}:${childIndex}`
9775
+ id: `${scope.id}:c${index}:${childIndex}`,
9776
+ measureTwips: cellMeasureTwips(cells[index], margins)
9596
9777
  })
9597
9778
  );
9598
9779
  return {
@@ -9607,12 +9788,7 @@ function compileColumns(component, scope) {
9607
9788
  }
9608
9789
  ],
9609
9790
  widthTwips: cells[index],
9610
- margins: {
9611
- topTwips: 0,
9612
- rightTwips: gaps[index] / 2,
9613
- bottomTwips: 0,
9614
- leftTwips: index > 0 ? gaps[index - 1] / 2 : 0
9615
- },
9791
+ margins,
9616
9792
  verticalAlign: "top",
9617
9793
  borders: {
9618
9794
  top: NO_BORDERS.top,
@@ -9669,6 +9845,16 @@ var DEFAULT_TEXT_BOX_WIDTH_TWIPS = 5e3;
9669
9845
  function compileTextBox(component, scope) {
9670
9846
  const { ctx, path: path4 } = scope;
9671
9847
  const props = component.props ?? {};
9848
+ const style = props.style;
9849
+ const padding = style?.padding;
9850
+ const width = textBoxWidth(props);
9851
+ const widthTwips = Math.round(tableWidthTwips(width, scope.measureTwips));
9852
+ const margins = {
9853
+ topTwips: padding?.top ? pointsToTwips2(padding.top) : 0,
9854
+ rightTwips: padding?.right ? pointsToTwips2(padding.right) : 0,
9855
+ bottomTwips: padding?.bottom ? pointsToTwips2(padding.bottom) : 0,
9856
+ leftTwips: padding?.left ? pointsToTwips2(padding.left) : 0
9857
+ };
9672
9858
  const children = [];
9673
9859
  const contents = component.children ?? [];
9674
9860
  contents.forEach((child, index) => {
@@ -9676,12 +9862,12 @@ function compileTextBox(component, scope) {
9676
9862
  ...compileComponent(child, {
9677
9863
  ...scope,
9678
9864
  path: `${path4}.children[${index}]`,
9679
- id: `${scope.id}:c${index}`
9865
+ id: `${scope.id}:c${index}`,
9866
+ // What the box holds is set against the box less its padding.
9867
+ measureTwips: cellMeasureTwips(widthTwips, margins)
9680
9868
  })
9681
9869
  );
9682
9870
  });
9683
- const style = props.style;
9684
- const padding = style?.padding;
9685
9871
  if (props.renderAs === "shape") {
9686
9872
  const shape = compileShape2(props, children, scope);
9687
9873
  if (shape) return shape;
@@ -9693,9 +9879,11 @@ function compileTextBox(component, scope) {
9693
9879
  kind: "table",
9694
9880
  id: scope.id,
9695
9881
  path: path4,
9696
- // No grid: one cell, whose width the table itself decides.
9697
- columnGrid: { unit: "twips", values: [] },
9698
- width: textBoxWidth(props),
9882
+ // One cell, as wide as the box. Left out, the grid would be each
9883
+ // backend's 100-twip placeholder, which a reader that takes the grid as
9884
+ // the column widths draws.
9885
+ columnGrid: { unit: "twips", values: [widthTwips] },
9886
+ width,
9699
9887
  layout: "fixed",
9700
9888
  // The container is invisible; whatever border the author asked for is
9701
9889
  // drawn by the cell inside it.
@@ -9715,12 +9903,7 @@ function compileTextBox(component, scope) {
9715
9903
  children: []
9716
9904
  }
9717
9905
  ],
9718
- margins: {
9719
- topTwips: padding?.top ? pointsToTwips2(padding.top) : 0,
9720
- rightTwips: padding?.right ? pointsToTwips2(padding.right) : 0,
9721
- bottomTwips: padding?.bottom ? pointsToTwips2(padding.bottom) : 0,
9722
- leftTwips: padding?.left ? pointsToTwips2(padding.left) : 0
9723
- },
9906
+ margins,
9724
9907
  ...style?.shading?.fill ? {
9725
9908
  shading: {
9726
9909
  fill: irColor(
@@ -9739,8 +9922,11 @@ function compileTextBox(component, scope) {
9739
9922
  var EMU_PER_PIXEL = 9525;
9740
9923
  function compileShape2(props, children, scope) {
9741
9924
  const { ctx, path: path4 } = scope;
9742
- const width = shapeSize(props.width, "width", ctx);
9743
- const height = shapeSize(props.height, "height", ctx);
9925
+ const width = shapeSize(props.width, scope.measureTwips);
9926
+ const height = shapeSize(
9927
+ props.height,
9928
+ getAvailableHeightTwips(ctx.theme, ctx.themeName)
9929
+ );
9744
9930
  if (width.pixels === void 0 || height.pixels === void 0) {
9745
9931
  warnOnce(
9746
9932
  ctx,
@@ -9771,7 +9957,7 @@ function compileShape2(props, children, scope) {
9771
9957
  warnOnce(
9772
9958
  ctx,
9773
9959
  "text-box",
9774
- '[core-docx] text-box renderAs "shape" resolves percentage sizes at generation time, against the current page content box; the shape will not reflow if the page size changes.'
9960
+ '[core-docx] text-box renderAs "shape" resolves percentage sizes at generation time \u2014 a width against what holds it, a height against the page \u2014 so the shape will not reflow if either changes.'
9775
9961
  );
9776
9962
  }
9777
9963
  if (outline.ignoredSides.length > 0) {
@@ -9816,7 +10002,7 @@ function compileShape2(props, children, scope) {
9816
10002
  }
9817
10003
  ];
9818
10004
  }
9819
- function shapeSize(value, axis, ctx) {
10005
+ function shapeSize(value, referenceTwips) {
9820
10006
  if (typeof value === "number") {
9821
10007
  return { pixels: Math.round(value), resolvedPercentage: false };
9822
10008
  }
@@ -9825,9 +10011,8 @@ function shapeSize(value, axis, ctx) {
9825
10011
  if (!Number.isFinite(fraction) || fraction <= 0) {
9826
10012
  return { resolvedPercentage: false };
9827
10013
  }
9828
- const availableTwips = axis === "width" ? getAvailableWidthTwips(ctx.theme, ctx.themeName) : getAvailableHeightTwips(ctx.theme, ctx.themeName);
9829
10014
  return {
9830
- pixels: Math.round(availableTwips * fraction / TWIPS_PER_PIXEL),
10015
+ pixels: Math.round(referenceTwips * fraction / TWIPS_PER_PIXEL),
9831
10016
  resolvedPercentage: true
9832
10017
  };
9833
10018
  }
@@ -9902,10 +10087,20 @@ function textBoxWidth(props) {
9902
10087
  value: typeof raw === "number" ? raw * TWIPS_PER_PIXEL : DEFAULT_TEXT_BOX_WIDTH_TWIPS
9903
10088
  };
9904
10089
  }
10090
+ function borderLineStyle(authored) {
10091
+ switch (authored) {
10092
+ case "dashed":
10093
+ case "dotted":
10094
+ case "double":
10095
+ return authored;
10096
+ default:
10097
+ return "single";
10098
+ }
10099
+ }
9905
10100
  function compileBorders(border3, ctx) {
9906
10101
  if (!border3) return void 0;
9907
10102
  const side = (value) => value ? {
9908
- style: String(value.style ?? "single"),
10103
+ style: value.style === "none" ? "none" : borderLineStyle(value.style),
9909
10104
  // Points to eighths of a point, never thinner than the hairline a
9910
10105
  // reader can actually see.
9911
10106
  sizeEighthPoints: value.width !== void 0 ? Math.max(1, Math.round(value.width * 8)) : 1,
@@ -10009,12 +10204,6 @@ function compileToc(component, scope) {
10009
10204
  var DEFAULT_DIVIDER_SPACING_PT = 6;
10010
10205
  var DEFAULT_DIVIDER_THICKNESS_PT = 1;
10011
10206
  var DIVIDER_LINE_TWIPS = 20;
10012
- var DIVIDER_BORDER_STYLE = {
10013
- solid: "single",
10014
- dashed: "dashed",
10015
- dotted: "dotted",
10016
- double: "double"
10017
- };
10018
10207
  function compileDivider(component, scope) {
10019
10208
  const { ctx, path: path4 } = scope;
10020
10209
  const props = component.props ?? {};
@@ -10043,10 +10232,10 @@ function compileDivider(component, scope) {
10043
10232
  lineTwips: DIVIDER_LINE_TWIPS,
10044
10233
  lineRule: "exact"
10045
10234
  },
10046
- ...dividerIndent(props, ctx, path4),
10235
+ ...dividerIndent(props, scope),
10047
10236
  borders: {
10048
10237
  bottom: {
10049
- style: DIVIDER_BORDER_STYLE[String(props.style)] ?? "single",
10238
+ style: borderLineStyle(props.style),
10050
10239
  sizeEighthPoints: pointsToEighthPoints(thicknessPt),
10051
10240
  color: irColor(
10052
10241
  resolveColor(String(props.color ?? "border"), ctx.theme)
@@ -10060,14 +10249,10 @@ function compileDivider(component, scope) {
10060
10249
  }
10061
10250
  ];
10062
10251
  }
10063
- function dividerIndent(props, ctx, path4) {
10252
+ function dividerIndent(props, scope) {
10253
+ const { ctx, path: path4, measureTwips } = scope;
10064
10254
  const width = props.width;
10065
10255
  if (width === void 0) return {};
10066
- const page = getPageSetup(ctx.theme);
10067
- const measureTwips = Math.max(
10068
- 0,
10069
- page.size.width - page.margin.left - page.margin.right
10070
- );
10071
10256
  let usedTwips;
10072
10257
  if (typeof width === "number" && Number.isFinite(width)) {
10073
10258
  usedTwips = pointsToTwips2(width);
@@ -10223,7 +10408,7 @@ function compileList(component, scope, referencePrefix = "list", explicitLevels)
10223
10408
  }
10224
10409
  blocks.push(
10225
10410
  paragraphNode(
10226
- { ctx, path: itemPath, id: `${scope.id}:i${index}` },
10411
+ { ...scope, path: itemPath, id: `${scope.id}:i${index}` },
10227
10412
  revision === void 0 ? parseInline(text, {
10228
10413
  base,
10229
10414
  hyperlinks: true,
@@ -10870,7 +11055,7 @@ function compileVisual(component, scope) {
10870
11055
  );
10871
11056
  if (!group) return [];
10872
11057
  const size = visualPlacementPixels(props, group.canvas, {
10873
- widthPx: Math.round(twipsToPixels(getAvailableWidthTwips(ctx.theme))),
11058
+ widthPx: Math.round(twipsToPixels(scope.measureTwips)),
10874
11059
  heightPx: Math.round(twipsToPixels(getAvailableHeightTwips(ctx.theme)))
10875
11060
  });
10876
11061
  const drawing = {
@@ -10985,7 +11170,7 @@ function compileImage2(component, scope) {
10985
11170
  `Alt text is not written to the document: the DOCX pipeline has never emitted \`wp:docPr\` descriptions, so the image at ${path4} will have none. The text is preserved in the IR.`
10986
11171
  );
10987
11172
  }
10988
- const size = imagePixelSize(props, loaded, ctx);
11173
+ const size = imagePixelSize(props, loaded, scope);
10989
11174
  const resourceId = declareResource(loaded, mediaType, ctx);
10990
11175
  const image = {
10991
11176
  kind: "image",
@@ -11026,6 +11211,24 @@ function compileImage2(component, scope) {
11026
11211
  return blocks;
11027
11212
  }
11028
11213
  var DEFAULT_CHART_HEIGHT_INCHES = 3;
11214
+ var MAX_CHART_TEXT_POINTS = 10;
11215
+ function chartTextFont(theme) {
11216
+ const bodySize = getThemeFonts(theme).body?.size;
11217
+ let color = "000000";
11218
+ try {
11219
+ color = resolveColor("textPrimary", theme);
11220
+ } catch {
11221
+ }
11222
+ return {
11223
+ fontFamily: resolveFontFamily(theme, "body"),
11224
+ fontSize: Math.min(
11225
+ typeof bodySize === "number" && bodySize > 0 ? bodySize : 11,
11226
+ MAX_CHART_TEXT_POINTS
11227
+ ),
11228
+ bold: false,
11229
+ color
11230
+ };
11231
+ }
11029
11232
  function compileChart(component, scope) {
11030
11233
  const { ctx, path: path4 } = scope;
11031
11234
  const props = component.props ?? {};
@@ -11077,8 +11280,7 @@ function compileChart(component, scope) {
11077
11280
  return [];
11078
11281
  }
11079
11282
  });
11080
- const page = getPageSetup(ctx.theme);
11081
- const contentWidthInches = (page.size.width - page.margin.left - page.margin.right) / 1440;
11283
+ const contentWidthInches = scope.measureTwips / 1440;
11082
11284
  const widthInches = typeof props.width === "number" ? props.width : contentWidthInches;
11083
11285
  const heightInches = typeof props.height === "number" ? props.height : DEFAULT_CHART_HEIGHT_INCHES;
11084
11286
  const chart = {
@@ -11094,6 +11296,7 @@ function compileChart(component, scope) {
11094
11296
  ...props.legendPos ? { legendPosition: props.legendPos } : {},
11095
11297
  ...typeof props.catAxisTitle === "string" ? { categoryAxisTitle: props.catAxisTitle } : {},
11096
11298
  ...typeof props.valAxisTitle === "string" ? { valueAxisTitle: props.valAxisTitle } : {},
11299
+ textFont: chartTextFont(ctx.theme),
11097
11300
  ...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {},
11098
11301
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path4) } : {}
11099
11302
  };
@@ -11177,12 +11380,13 @@ function wrapType(value) {
11177
11380
  return "square";
11178
11381
  }
11179
11382
  }
11180
- function imagePixelSize(props, loaded, ctx) {
11383
+ function imagePixelSize(props, loaded, scope) {
11384
+ const { ctx } = scope;
11181
11385
  const widthRef = props.widthRelativeTo === "page" ? "page" : "content";
11182
11386
  const heightRef = props.heightRelativeTo === "page" ? "page" : "content";
11183
11387
  const availableWidthPx = Math.round(
11184
11388
  twipsToPixels(
11185
- widthRef === "page" ? getPageWidthTwips(ctx.theme) : getAvailableWidthTwips(ctx.theme)
11389
+ widthRef === "page" ? getPageWidthTwips(ctx.theme) : scope.measureTwips
11186
11390
  )
11187
11391
  );
11188
11392
  const availableHeightPx = Math.round(
@@ -11369,7 +11573,10 @@ function compileTable(component, scope) {
11369
11573
  source,
11370
11574
  ctx.theme,
11371
11575
  ctx.themeName,
11372
- { onWarning: (code, message) => warnOnce(ctx, "table", message, code) }
11576
+ {
11577
+ onWarning: (code, message) => warnOnce(ctx, "table", message, code),
11578
+ measureTwips: scope.measureTwips
11579
+ }
11373
11580
  );
11374
11581
  if (model.overflow) {
11375
11582
  warnOnce(
@@ -11387,6 +11594,7 @@ function compileTable(component, scope) {
11387
11594
  ...model.header ? [
11388
11595
  compileTableRow(
11389
11596
  model.header,
11597
+ model.columnGrid.values,
11390
11598
  style.headerParagraph,
11391
11599
  style.tableHeader,
11392
11600
  {
@@ -11399,13 +11607,19 @@ function compileTable(component, scope) {
11399
11607
  )
11400
11608
  ] : [],
11401
11609
  ...model.rows.map(
11402
- (row, index) => compileTableRow(row, style.cellParagraph, style.tableCell, {
11403
- ctx,
11404
- path: `${path4}.rows[${index}]`,
11405
- id: `${scope.id}:r${index}`,
11406
- ...row.tableHeader !== void 0 ? { isHeader: row.tableHeader } : {},
11407
- ...row.cantSplit !== void 0 ? { cantSplit: row.cantSplit } : {}
11408
- })
11610
+ (row, index) => compileTableRow(
11611
+ row,
11612
+ model.columnGrid.values,
11613
+ style.cellParagraph,
11614
+ style.tableCell,
11615
+ {
11616
+ ctx,
11617
+ path: `${path4}.rows[${index}]`,
11618
+ id: `${scope.id}:r${index}`,
11619
+ ...row.tableHeader !== void 0 ? { isHeader: row.tableHeader } : {},
11620
+ ...row.cantSplit !== void 0 ? { cantSplit: row.cantSplit } : {}
11621
+ }
11622
+ )
11409
11623
  )
11410
11624
  ];
11411
11625
  ctx.features.require("tables", path4);
@@ -11483,7 +11697,7 @@ function tableBlocker(model, path4) {
11483
11697
  }
11484
11698
  return void 0;
11485
11699
  }
11486
- function compileTableRow(row, spacing, baseStyle, scope) {
11700
+ function compileTableRow(row, columnTwips, spacing, baseStyle, scope) {
11487
11701
  const { ctx } = scope;
11488
11702
  const rowRevision = row.revision;
11489
11703
  const rowMark = rowRevision ? {
@@ -11503,6 +11717,7 @@ function compileTableRow(row, spacing, baseStyle, scope) {
11503
11717
  cells: row.cells.map(
11504
11718
  (cell, colIndex) => compileTableCell(
11505
11719
  cell,
11720
+ columnTwips[colIndex] ?? 0,
11506
11721
  spacing,
11507
11722
  baseStyle,
11508
11723
  row.keepNext,
@@ -11524,7 +11739,17 @@ function compileTableRow(row, spacing, baseStyle, scope) {
11524
11739
  ...scope.cantSplit !== void 0 ? { cantSplit: scope.cantSplit } : {}
11525
11740
  };
11526
11741
  }
11527
- function compileTableCell(cell, spacing, baseStyle, keepNext, scope, rowRevision, markRevision) {
11742
+ function compileTableCell(cell, columnTwips, spacing, baseStyle, keepNext, partScope, rowRevision, markRevision) {
11743
+ const margins = !cell.missing && cell.padding ? {
11744
+ topTwips: pointsToTwips2(cell.padding.top),
11745
+ bottomTwips: pointsToTwips2(cell.padding.bottom),
11746
+ leftTwips: pointsToTwips2(cell.padding.left),
11747
+ rightTwips: pointsToTwips2(cell.padding.right)
11748
+ } : void 0;
11749
+ const scope = {
11750
+ ...partScope,
11751
+ measureTwips: cellMeasureTwips(columnTwips, margins)
11752
+ };
11528
11753
  const cellLine = compileLineSpacing(cell.font?.lineSpacing);
11529
11754
  const paragraph2 = {
11530
11755
  kind: "paragraph",
@@ -11553,14 +11778,7 @@ function compileTableCell(cell, spacing, baseStyle, keepNext, scope, rowRevision
11553
11778
  ...cell.missing ? {} : {
11554
11779
  verticalAlign: verticalAlign(cell.verticalAlignment),
11555
11780
  ...cell.backgroundColor !== void 0 && cell.backgroundColor !== "transparent" ? { shading: { fill: { hex: cell.backgroundColor } } } : {},
11556
- ...cell.padding ? {
11557
- margins: {
11558
- topTwips: pointsToTwips2(cell.padding.top),
11559
- bottomTwips: pointsToTwips2(cell.padding.bottom),
11560
- leftTwips: pointsToTwips2(cell.padding.left),
11561
- rightTwips: pointsToTwips2(cell.padding.right)
11562
- }
11563
- } : {}
11781
+ ...margins ? { margins } : {}
11564
11782
  },
11565
11783
  borders: {
11566
11784
  top: compileTableBorder(cell.borders.top),
@@ -11832,6 +12050,18 @@ init_styles();
11832
12050
  init_widthUtils();
11833
12051
  function applyLayout(sections, theme, themeName) {
11834
12052
  const layoutSections = [];
12053
+ const push = (layoutSection) => {
12054
+ const previous = layoutSections[layoutSections.length - 1];
12055
+ if (previous && layoutSection.properties.type === "continuous" && pageSizeDiffers(
12056
+ previous.properties.page.size,
12057
+ layoutSection.properties.page.size
12058
+ )) {
12059
+ layoutSection.properties.type = "nextPage";
12060
+ layoutSection.breakBefore = true;
12061
+ layoutSection.forcedPageBreak = true;
12062
+ }
12063
+ layoutSections.push(layoutSection);
12064
+ };
11835
12065
  for (let i = 0; i < sections.length; i++) {
11836
12066
  const section2 = sections[i];
11837
12067
  const layoutGroups = analyzeLayoutGroups(section2.components);
@@ -11840,7 +12070,7 @@ function applyLayout(sections, theme, themeName) {
11840
12070
  const isFirstSection = i === 0;
11841
12071
  const breakBefore = isFirstSection || Boolean(section2.pageBreak);
11842
12072
  const sectionType2 = breakBefore ? "nextPage" : "continuous";
11843
- layoutSections.push({
12073
+ push({
11844
12074
  properties: createSectionProperties(
11845
12075
  getColumnSettings("single"),
11846
12076
  theme,
@@ -11862,7 +12092,12 @@ function applyLayout(sections, theme, themeName) {
11862
12092
  for (let j = 0; j < layoutGroups.length; j++) {
11863
12093
  const group = layoutGroups[j];
11864
12094
  const isSingleColumnsGroup = group.components.length === 1 && isColumnsComponent(group.components[0]);
11865
- const columnSettings = isSingleColumnsGroup ? createColumnSettingsFromConfig(group.components[0], theme, themeName) : getColumnSettings(group.layout);
12095
+ const columnSettings = isSingleColumnsGroup ? createColumnSettingsFromConfig(
12096
+ group.components[0],
12097
+ // Percentages are of this section's text measure, not the theme's.
12098
+ theme ? themeForSectionPage(theme, section2.page) : theme,
12099
+ themeName
12100
+ ) : getColumnSettings(group.layout);
11866
12101
  const isFirstSection = i === 0 && j === 0;
11867
12102
  const titlelessSectionBreak = Boolean(section2.pageBreak && j === 0);
11868
12103
  const breakBefore = group.breakBefore || isFirstSection || titlelessSectionBreak;
@@ -11870,7 +12105,7 @@ function applyLayout(sections, theme, themeName) {
11870
12105
  const contentComponents = isSingleColumnsGroup ? processLayoutComponents(
11871
12106
  group.components[0].children || []
11872
12107
  ) : processLayoutComponents(group.components);
11873
- layoutSections.push({
12108
+ push({
11874
12109
  properties: createSectionProperties(
11875
12110
  columnSettings,
11876
12111
  theme,
@@ -12737,9 +12972,10 @@ function docxBlockContext(document, theme, path4, sources) {
12737
12972
  page.size ?? theme.page.size
12738
12973
  );
12739
12974
  const overrides = isBlockRecord(page.margins) ? page.margins : {};
12975
+ const gutter = "gutter" in margins ? margins.gutter : 0;
12740
12976
  const width = Object.keys(page).length ? Math.max(
12741
12977
  0,
12742
- dimensions.width - Number(overrides.left ?? margins.left ?? 1440) - Number(overrides.right ?? margins.right ?? 1440)
12978
+ dimensions.width - Number(overrides.left ?? margins.left ?? 1440) - Number(overrides.right ?? margins.right ?? 1440) - Number(overrides.gutter ?? gutter ?? 0)
12743
12979
  ) : getAvailableWidthTwips(theme);
12744
12980
  const height = Object.keys(page).length ? Math.max(
12745
12981
  0,
@@ -13320,7 +13556,7 @@ function pageBox(theme, themeName, pageOverride) {
13320
13556
  return {
13321
13557
  availableWidthTwips: Math.max(
13322
13558
  0,
13323
- page.size.width - page.margin.left - page.margin.right
13559
+ page.size.width - page.margin.left - page.margin.right - (page.margin.gutter ?? 0)
13324
13560
  ),
13325
13561
  pageBottomTwips: page.size.height
13326
13562
  };