@json-to-office/core-docx 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1813,6 +1813,67 @@ import { Packer as Packer2 } from "docx";
1813
1813
  // src/plugin/createDocumentGenerator.ts
1814
1814
  init_styles();
1815
1815
  import { Packer } from "docx";
1816
+ import { applyExportMode, scopedThemeName } from "@json-to-office/shared";
1817
+
1818
+ // src/core/fontResolution.ts
1819
+ import {
1820
+ collectFontNamesFromDocx,
1821
+ validateFontReferences,
1822
+ FontRegistry
1823
+ } from "@json-to-office/shared";
1824
+ import {
1825
+ loadFileFontSource,
1826
+ FontDiskCache,
1827
+ fetchVariableFontSource
1828
+ } from "@json-to-office/shared/fonts/node";
1829
+ async function resolveDocumentFonts(document, theme, fonts, warnings) {
1830
+ const emit = (code, message) => {
1831
+ if (warnings) {
1832
+ warnings.push({
1833
+ component: "fontRegistry",
1834
+ message,
1835
+ severity: "warning",
1836
+ context: { code }
1837
+ });
1838
+ } else {
1839
+ console.warn(`[json-to-docx] ${code}: ${message}`);
1840
+ }
1841
+ };
1842
+ const names = /* @__PURE__ */ new Set();
1843
+ for (const n of collectFontNamesFromDocx(document)) names.add(n);
1844
+ for (const n of collectFontNamesFromDocx(theme)) names.add(n);
1845
+ if (names.size === 0) return [];
1846
+ const validation = validateFontReferences({
1847
+ referencedNames: names,
1848
+ registeredEntries: fonts?.extraEntries
1849
+ });
1850
+ if (validation.warnings.length > 0) {
1851
+ if (fonts?.strict) {
1852
+ throw new Error(
1853
+ `Unresolved font references (strict mode):
1854
+ ` + validation.warnings.map((w) => ` - ${w.message}`).join("\n")
1855
+ );
1856
+ }
1857
+ for (const w of validation.warnings) {
1858
+ emit(w.code, w.message);
1859
+ }
1860
+ }
1861
+ if (!fonts?.onResolved) return [];
1862
+ const registry = new FontRegistry({
1863
+ opts: fonts,
1864
+ fileLoader: loadFileFontSource,
1865
+ variableLoader: fetchVariableFontSource,
1866
+ diskCache: fonts?.googleFonts?.cacheDir ? new FontDiskCache(fonts.googleFonts.cacheDir) : void 0
1867
+ });
1868
+ const resolved = await registry.resolveMany(names);
1869
+ for (const r of resolved) {
1870
+ for (const msg of r.warnings) {
1871
+ emit("FONT_UNRESOLVED", msg);
1872
+ }
1873
+ }
1874
+ fonts.onResolved(resolved);
1875
+ return resolved;
1876
+ }
1816
1877
 
1817
1878
  // src/plugin/version-resolver.ts
1818
1879
  import { resolveComponentVersion } from "@json-to-office/shared/plugin";
@@ -1969,24 +2030,175 @@ var formatDate = (date, formatString = "MMMM d, yyyy") => {
1969
2030
  return format(date, formatString);
1970
2031
  };
1971
2032
 
2033
+ // src/styles/utils/componentDefaults.ts
2034
+ import { mergeWithDefaults } from "@json-to-office/shared";
2035
+ import { mergeWithDefaults as mergeWithDefaults2 } from "@json-to-office/shared";
2036
+ function getComponentDefaults(theme) {
2037
+ return theme.componentDefaults || {};
2038
+ }
2039
+ function getHeadingDefaults(theme) {
2040
+ const defaults = getComponentDefaults(theme);
2041
+ return defaults?.heading || {};
2042
+ }
2043
+ function getHeadingDefaultsForLevel(theme, level) {
2044
+ const defaults = {};
2045
+ if (theme.styles) {
2046
+ const styleKey = `heading${level}`;
2047
+ const headingStyle = theme.styles[styleKey];
2048
+ if (headingStyle?.alignment) {
2049
+ defaults.alignment = headingStyle.alignment;
2050
+ }
2051
+ }
2052
+ return defaults;
2053
+ }
2054
+ function getTextDefaults(theme) {
2055
+ const defaults = getComponentDefaults(theme);
2056
+ return defaults?.paragraph || {};
2057
+ }
2058
+ function getImageDefaults(theme) {
2059
+ const defaults = getComponentDefaults(theme);
2060
+ return defaults?.image || {};
2061
+ }
2062
+ function getStatisticDefaults(theme) {
2063
+ const defaults = getComponentDefaults(theme);
2064
+ return defaults?.statistic || {};
2065
+ }
2066
+ function getTableDefaults(theme) {
2067
+ const defaults = getComponentDefaults(theme);
2068
+ return defaults?.table || {};
2069
+ }
2070
+ function getSectionDefaults(theme) {
2071
+ const defaults = getComponentDefaults(theme);
2072
+ return defaults?.section || {};
2073
+ }
2074
+ function getColumnsDefaults(theme) {
2075
+ const defaults = getComponentDefaults(theme);
2076
+ return defaults?.columns || {};
2077
+ }
2078
+ function getListDefaults(theme) {
2079
+ const defaults = getComponentDefaults(theme);
2080
+ return defaults?.list || {};
2081
+ }
2082
+ function resolveHeadingProps(props, theme) {
2083
+ const defaults = getHeadingDefaults(theme);
2084
+ return mergeWithDefaults(props, defaults);
2085
+ }
2086
+ function resolveParagraphProps(props, theme) {
2087
+ const defaults = getTextDefaults(theme);
2088
+ return mergeWithDefaults(props, defaults);
2089
+ }
2090
+ function resolveImageProps(props, theme) {
2091
+ const defaults = getImageDefaults(theme);
2092
+ return mergeWithDefaults(props, defaults);
2093
+ }
2094
+ function resolveStatisticProps(props, theme) {
2095
+ const defaults = getStatisticDefaults(theme);
2096
+ return mergeWithDefaults(props, defaults);
2097
+ }
2098
+ function resolveTableProps(props, theme) {
2099
+ const defaults = getTableDefaults(theme);
2100
+ return mergeWithDefaults(props, defaults);
2101
+ }
2102
+ function resolveSectionProps(props, theme) {
2103
+ const defaults = getSectionDefaults(theme);
2104
+ return mergeWithDefaults(props, defaults);
2105
+ }
2106
+ function resolveColumnsProps(props, theme) {
2107
+ const defaults = getColumnsDefaults(theme);
2108
+ return mergeWithDefaults(props, defaults);
2109
+ }
2110
+ function resolveListProps(props, theme) {
2111
+ const defaults = getListDefaults(theme);
2112
+ return mergeWithDefaults(props, defaults);
2113
+ }
2114
+ function resolveHighchartsProps(props, _theme) {
2115
+ return props;
2116
+ }
2117
+ function getCustomComponentDefaults(theme, componentName) {
2118
+ const defaults = getComponentDefaults(theme);
2119
+ return defaults?.[componentName] || {};
2120
+ }
2121
+ function resolveCustomComponentProps(props, theme, componentName) {
2122
+ const defaults = getCustomComponentDefaults(theme, componentName);
2123
+ return mergeWithDefaults(props, defaults);
2124
+ }
2125
+
2126
+ // src/styles/utils/resolveComponentTree.ts
2127
+ function resolveHeadingWithLevelDefaults(props, theme) {
2128
+ const resolved = resolveHeadingProps(props, theme);
2129
+ const level = resolved.level || 1;
2130
+ const levelDefaults = getHeadingDefaultsForLevel(theme, level);
2131
+ return {
2132
+ ...resolved,
2133
+ // Only apply level-specific defaults if no explicit alignment in original props
2134
+ ...props.alignment ? {} : levelDefaults
2135
+ };
2136
+ }
2137
+ var RESOLVER_MAP = {
2138
+ heading: resolveHeadingWithLevelDefaults,
2139
+ paragraph: resolveParagraphProps,
2140
+ image: resolveImageProps,
2141
+ statistic: resolveStatisticProps,
2142
+ table: resolveTableProps,
2143
+ section: resolveSectionProps,
2144
+ columns: resolveColumnsProps,
2145
+ list: resolveListProps,
2146
+ highcharts: resolveHighchartsProps
2147
+ };
2148
+ function resolveComponentDefaults(component, theme) {
2149
+ if (!component.props) return component;
2150
+ const resolver = RESOLVER_MAP[component.name];
2151
+ const resolvedProps = resolver ? resolver(component.props, theme) : resolveCustomComponentProps(
2152
+ component.props,
2153
+ theme,
2154
+ component.name
2155
+ );
2156
+ return { ...component, props: resolvedProps };
2157
+ }
2158
+ function resolveComponentTree(components, theme) {
2159
+ return components.map((component) => {
2160
+ const resolved = resolveComponentDefaults(component, theme);
2161
+ const children = resolved.children;
2162
+ if (children && children.length > 0) {
2163
+ return {
2164
+ ...resolved,
2165
+ children: resolveComponentTree(children, theme)
2166
+ };
2167
+ }
2168
+ return resolved;
2169
+ });
2170
+ }
2171
+
1972
2172
  // src/core/structure.ts
1973
2173
  async function processDocument(document, theme, themeName) {
1974
2174
  const metadata = createDocumentMetadata(document.props);
2175
+ const docDefaults = document.props.componentDefaults;
2176
+ const effectiveTheme = docDefaults ? {
2177
+ ...theme,
2178
+ componentDefaults: mergeWithDefaults2(
2179
+ docDefaults,
2180
+ theme.componentDefaults || {}
2181
+ )
2182
+ } : theme;
1975
2183
  const context = createRenderContext(
1976
2184
  {
1977
2185
  metadata,
1978
2186
  sections: [],
1979
- theme,
2187
+ theme: effectiveTheme,
1980
2188
  themeName
1981
2189
  },
1982
- theme,
2190
+ effectiveTheme,
1983
2191
  themeName
1984
2192
  );
1985
- const sections = await extractSections(document.children || [], context);
2193
+ const resolvedChildren = resolveComponentTree(
2194
+ document.children || [],
2195
+ effectiveTheme
2196
+ );
2197
+ const sections = await extractSections(resolvedChildren, context);
1986
2198
  return {
1987
2199
  metadata,
1988
2200
  sections,
1989
- theme,
2201
+ theme: effectiveTheme,
1990
2202
  themeName
1991
2203
  };
1992
2204
  }
@@ -2016,19 +2228,24 @@ async function extractSections(components, context) {
2016
2228
  Math.max(component.props.level || 1, 1),
2017
2229
  6
2018
2230
  );
2019
- sectionComponents.unshift({
2020
- name: "heading",
2021
- props: {
2022
- text: component.props.title,
2023
- level: headingLevel,
2024
- pageBreak: shouldPageBreak,
2025
- // Apply zero-spacing to prevent unwanted initial line
2026
- spacing: {
2027
- before: 0,
2028
- after: 0
2029
- }
2030
- }
2031
- });
2231
+ sectionComponents.unshift(
2232
+ resolveComponentDefaults(
2233
+ {
2234
+ name: "heading",
2235
+ props: {
2236
+ text: component.props.title,
2237
+ level: headingLevel,
2238
+ pageBreak: shouldPageBreak,
2239
+ // Apply zero-spacing to prevent unwanted initial line
2240
+ spacing: {
2241
+ before: 0,
2242
+ after: 0
2243
+ }
2244
+ }
2245
+ },
2246
+ context.fullTheme
2247
+ )
2248
+ );
2032
2249
  }
2033
2250
  sections.push({
2034
2251
  title: component.props?.title,
@@ -2068,18 +2285,23 @@ async function flattenComponents(components, context) {
2068
2285
  Math.max(component.props.level || 1, 1),
2069
2286
  6
2070
2287
  );
2071
- flattened.push({
2072
- name: "heading",
2073
- props: {
2074
- text: component.props.title,
2075
- level: headingLevel,
2076
- // Apply zero-spacing to prevent unwanted initial line
2077
- spacing: {
2078
- before: 0,
2079
- after: 0
2080
- }
2081
- }
2082
- });
2288
+ flattened.push(
2289
+ resolveComponentDefaults(
2290
+ {
2291
+ name: "heading",
2292
+ props: {
2293
+ text: component.props.title,
2294
+ level: headingLevel,
2295
+ // Apply zero-spacing to prevent unwanted initial line
2296
+ spacing: {
2297
+ before: 0,
2298
+ after: 0
2299
+ }
2300
+ }
2301
+ },
2302
+ context.fullTheme
2303
+ )
2304
+ );
2083
2305
  }
2084
2306
  flattened.push(...await flattenComponents(component.children, context));
2085
2307
  } else {
@@ -3844,93 +4066,6 @@ async function renderComponentWithCache(component, theme, themeName, context, by
3844
4066
  return rendered;
3845
4067
  }
3846
4068
 
3847
- // src/styles/utils/componentDefaults.ts
3848
- function getComponentDefaults(theme) {
3849
- return theme.componentDefaults || {};
3850
- }
3851
- function getHeadingDefaults(theme) {
3852
- const defaults = getComponentDefaults(theme);
3853
- return defaults?.heading || {};
3854
- }
3855
- function getHeadingDefaultsForLevel(theme, level) {
3856
- const defaults = {};
3857
- if (theme.styles) {
3858
- const styleKey = `heading${level}`;
3859
- const headingStyle = theme.styles[styleKey];
3860
- if (headingStyle?.alignment) {
3861
- defaults.alignment = headingStyle.alignment;
3862
- }
3863
- }
3864
- return defaults;
3865
- }
3866
- function getTextDefaults(theme) {
3867
- const defaults = getComponentDefaults(theme);
3868
- return defaults?.paragraph || {};
3869
- }
3870
- function getImageDefaults(theme) {
3871
- const defaults = getComponentDefaults(theme);
3872
- return defaults?.image || {};
3873
- }
3874
- function getStatisticDefaults(theme) {
3875
- const defaults = getComponentDefaults(theme);
3876
- return defaults?.statistic || {};
3877
- }
3878
- function getColumnsDefaults(theme) {
3879
- const defaults = getComponentDefaults(theme);
3880
- return defaults?.columns || {};
3881
- }
3882
- function getListDefaults(theme) {
3883
- const defaults = getComponentDefaults(theme);
3884
- return defaults?.list || {};
3885
- }
3886
- function deepMerge(target, source) {
3887
- const output = { ...target };
3888
- if (isObject(target) && isObject(source)) {
3889
- Object.keys(source).forEach((key) => {
3890
- if (isObject(source[key])) {
3891
- if (!(key in target)) {
3892
- output[key] = source[key];
3893
- } else {
3894
- output[key] = deepMerge(target[key], source[key]);
3895
- }
3896
- } else {
3897
- output[key] = source[key];
3898
- }
3899
- });
3900
- }
3901
- return output;
3902
- }
3903
- function isObject(item) {
3904
- return item !== null && typeof item === "object" && !Array.isArray(item);
3905
- }
3906
- function mergeWithDefaults(userConfig, themeDefaults) {
3907
- return deepMerge(themeDefaults, userConfig);
3908
- }
3909
- function resolveHeadingProps(props, theme) {
3910
- const defaults = getHeadingDefaults(theme);
3911
- return mergeWithDefaults(props, defaults);
3912
- }
3913
- function resolveParagraphProps(props, theme) {
3914
- const defaults = getTextDefaults(theme);
3915
- return mergeWithDefaults(props, defaults);
3916
- }
3917
- function resolveImageProps(props, theme) {
3918
- const defaults = getImageDefaults(theme);
3919
- return mergeWithDefaults(props, defaults);
3920
- }
3921
- function resolveStatisticProps(props, theme) {
3922
- const defaults = getStatisticDefaults(theme);
3923
- return mergeWithDefaults(props, defaults);
3924
- }
3925
- function resolveColumnsProps(props, theme) {
3926
- const defaults = getColumnsDefaults(theme);
3927
- return mergeWithDefaults(props, defaults);
3928
- }
3929
- function resolveListProps(props, theme) {
3930
- const defaults = getListDefaults(theme);
3931
- return mergeWithDefaults(props, defaults);
3932
- }
3933
-
3934
4069
  // src/core/content.ts
3935
4070
  import {
3936
4071
  Paragraph,
@@ -4024,9 +4159,22 @@ var globalBookmarkRegistry = new BookmarkRegistry();
4024
4159
 
4025
4160
  // src/core/content.ts
4026
4161
  init_styleHelpers();
4162
+ import { synthesizeFamilyName } from "@json-to-office/shared";
4027
4163
  init_colorUtils();
4028
4164
  init_styleHelpers();
4029
4165
  init_widthUtils();
4166
+ function applyFontWeightAlias(opts) {
4167
+ if (!opts.fontFamily) {
4168
+ return { font: void 0, bold: opts.bold, italics: opts.italic };
4169
+ }
4170
+ const weight = opts.fontWeight ?? (opts.bold === true ? 700 : void 0);
4171
+ const synth = synthesizeFamilyName(
4172
+ opts.fontFamily,
4173
+ weight,
4174
+ opts.italic === true
4175
+ );
4176
+ return { font: synth.family, bold: synth.bold, italics: synth.italic };
4177
+ }
4030
4178
  function createText(content, theme, themeName, options = {}) {
4031
4179
  const normalizedContent = normalizeUnicodeText(content);
4032
4180
  const style = options.style || "Normal";
@@ -4046,15 +4194,27 @@ function createText(content, theme, themeName, options = {}) {
4046
4194
  if (options.columnBreak) {
4047
4195
  children.push(new ColumnBreak());
4048
4196
  }
4197
+ const hasWeightRequest = options.fontWeight != null || options.bold === true;
4198
+ const effectiveFamily = options.fontFamily ?? (hasWeightRequest ? resolveFontFamily(theme, "body") : void 0);
4199
+ const weighted = applyFontWeightAlias({
4200
+ fontFamily: effectiveFamily,
4201
+ bold: options.bold,
4202
+ italic: options.italic,
4203
+ fontWeight: options.fontWeight
4204
+ });
4049
4205
  const baseTextStyle = {
4050
- ...options.fontFamily && { font: options.fontFamily },
4206
+ // Only emit `font` when it came from the caller or from an alias —
4207
+ // emitting the theme body family on every run would be a behavior change.
4208
+ ...(options.fontFamily || weighted.font && weighted.font !== effectiveFamily) && {
4209
+ font: weighted.font
4210
+ },
4051
4211
  ...options.fontSize && { size: options.fontSize * 2 },
4052
4212
  // Convert points to half-points
4053
4213
  ...options.fontColor && {
4054
4214
  color: resolveColor(options.fontColor, theme)
4055
4215
  },
4056
- ...options.bold !== void 0 && { bold: options.bold },
4057
- ...options.italic !== void 0 && { italics: options.italic },
4216
+ ...weighted.bold !== void 0 && { bold: weighted.bold },
4217
+ ...weighted.italics !== void 0 && { italics: weighted.italics },
4058
4218
  ...options.underline !== void 0 && {
4059
4219
  underline: options.underline ? { type: "single" } : void 0
4060
4220
  }
@@ -4174,13 +4334,25 @@ function createHeading(text, level, theme, _themeName, options = {}) {
4174
4334
  children.push(new ColumnBreak());
4175
4335
  }
4176
4336
  const hasDecorators = /(\*\*\*|___|(\*\*|__)|(\*|_))/.test(normalizedText);
4337
+ const headingHasWeightRequest = options.fontWeight != null || options.bold === true;
4338
+ const headingEffectiveFamily = options.fontFamily ?? (headingHasWeightRequest ? resolveFontFamily(theme, "heading") : void 0);
4339
+ const headingWeighted = applyFontWeightAlias({
4340
+ fontFamily: headingEffectiveFamily,
4341
+ bold: options.bold,
4342
+ italic: options.italic,
4343
+ fontWeight: options.fontWeight
4344
+ });
4177
4345
  const baseTextStyle = {
4178
- ...options.fontFamily && { font: options.fontFamily },
4346
+ ...(options.fontFamily || headingWeighted.font && headingWeighted.font !== headingEffectiveFamily) && {
4347
+ font: headingWeighted.font
4348
+ },
4179
4349
  ...options.fontSize && { size: options.fontSize * 2 },
4180
4350
  // points to half-points
4181
4351
  ...options.fontColor && { color: resolveColor(options.fontColor, theme) },
4182
- ...options.bold !== void 0 && { bold: options.bold },
4183
- ...options.italic !== void 0 && { italics: options.italic },
4352
+ ...headingWeighted.bold !== void 0 && { bold: headingWeighted.bold },
4353
+ ...headingWeighted.italics !== void 0 && {
4354
+ italics: headingWeighted.italics
4355
+ },
4184
4356
  ...options.underline !== void 0 && {
4185
4357
  underline: options.underline ? { type: "single" } : void 0
4186
4358
  }
@@ -4692,12 +4864,18 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4692
4864
  if (!cell) {
4693
4865
  return cellChildren;
4694
4866
  }
4867
+ const cellWeighted = applyFontWeightAlias({
4868
+ fontFamily: cellDefaults.font?.family || baseCellStyle.font,
4869
+ bold: cellDefaults.font?.bold ?? false,
4870
+ italic: cellDefaults.font?.italic ?? false,
4871
+ fontWeight: cellDefaults.font?.fontWeight
4872
+ });
4695
4873
  const mergedStyle = {
4696
- font: cellDefaults.font?.family || baseCellStyle.font,
4874
+ font: cellWeighted.font,
4697
4875
  size: cellDefaults.font?.size ? cellDefaults.font.size * 2 : baseCellStyle.size,
4698
4876
  // Convert to half-points
4699
- bold: cellDefaults.font?.bold ?? false,
4700
- italics: cellDefaults.font?.italic ?? false,
4877
+ bold: cellWeighted.bold ?? false,
4878
+ italics: cellWeighted.italics ?? false,
4701
4879
  underline: cellDefaults.font?.underline ? { type: "single" } : void 0,
4702
4880
  color: cellDefaults.color || baseCellStyle.color
4703
4881
  };
@@ -4705,15 +4883,21 @@ async function createTable(columns, tableConfig, theme, themeName, _options = {}
4705
4883
  if (isParagraphComponent(cell)) {
4706
4884
  const textComp = cell;
4707
4885
  const paragraphFont = textComp.props.font;
4886
+ const paraWeighted = applyFontWeightAlias({
4887
+ fontFamily: paragraphFont?.family ?? mergedStyle.font,
4888
+ bold: paragraphFont?.bold,
4889
+ italic: paragraphFont?.italic,
4890
+ fontWeight: paragraphFont?.fontWeight
4891
+ });
4708
4892
  const paragraphStyle = {
4709
4893
  ...mergedStyle,
4710
- ...paragraphFont?.family && { font: paragraphFont.family },
4894
+ ...paraWeighted.font && { font: paraWeighted.font },
4711
4895
  ...paragraphFont?.size && { size: paragraphFont.size * 2 },
4712
- ...paragraphFont?.bold !== void 0 && {
4713
- bold: paragraphFont.bold
4896
+ ...paraWeighted.bold !== void 0 && {
4897
+ bold: paraWeighted.bold
4714
4898
  },
4715
- ...paragraphFont?.italic !== void 0 && {
4716
- italics: paragraphFont.italic
4899
+ ...paraWeighted.italics !== void 0 && {
4900
+ italics: paraWeighted.italics
4717
4901
  },
4718
4902
  ...paragraphFont?.underline !== void 0 && {
4719
4903
  underline: paragraphFont.underline ? { type: "single" } : void 0
@@ -5144,35 +5328,29 @@ function createFooterElement(children, _options) {
5144
5328
  // src/components/heading.ts
5145
5329
  function renderHeadingComponent(component, theme, themeName) {
5146
5330
  if (!isHeadingComponent(component)) return [];
5147
- const resolvedConfig = resolveHeadingProps(component.props, theme);
5148
- const level = resolvedConfig.level || 1;
5149
- const levelDefaults = getHeadingDefaultsForLevel(theme, level);
5150
- const finalConfig = {
5151
- ...resolvedConfig,
5152
- // Only apply level defaults if no explicit alignment was provided in the original props
5153
- ...component.props.alignment ? {} : levelDefaults
5154
- };
5155
- const bookmarkId = component.id || globalBookmarkRegistry.generateId(finalConfig.text, "heading");
5331
+ const config = component.props;
5332
+ const bookmarkId = component.id || globalBookmarkRegistry.generateId(config.text, "heading");
5156
5333
  const header = createHeading(
5157
- finalConfig.text,
5158
- finalConfig.level || 1,
5334
+ config.text,
5335
+ config.level || 1,
5159
5336
  theme,
5160
5337
  themeName,
5161
5338
  {
5162
- alignment: finalConfig.alignment,
5163
- spacing: finalConfig.spacing,
5164
- lineSpacing: finalConfig.lineSpacing,
5165
- columnBreak: finalConfig.columnBreak,
5339
+ alignment: config.alignment,
5340
+ spacing: config.spacing,
5341
+ lineSpacing: config.lineSpacing,
5342
+ columnBreak: config.columnBreak,
5166
5343
  // Local font overrides
5167
- fontFamily: finalConfig.font?.family,
5168
- fontSize: finalConfig.font?.size,
5169
- fontColor: finalConfig.font?.color,
5170
- bold: finalConfig.font?.bold,
5171
- italic: finalConfig.font?.italic,
5172
- underline: finalConfig.font?.underline,
5344
+ fontFamily: config.font?.family,
5345
+ fontSize: config.font?.size,
5346
+ fontColor: config.font?.color,
5347
+ bold: config.font?.bold,
5348
+ fontWeight: config.font?.fontWeight,
5349
+ italic: config.font?.italic,
5350
+ underline: config.font?.underline,
5173
5351
  // Pagination control
5174
- keepNext: finalConfig.keepNext,
5175
- keepLines: finalConfig.keepLines,
5352
+ keepNext: config.keepNext,
5353
+ keepLines: config.keepLines,
5176
5354
  // Bookmark ID for internal linking
5177
5355
  bookmarkId
5178
5356
  }
@@ -5217,7 +5395,7 @@ function parseMarkdownList(text) {
5217
5395
  }
5218
5396
  function renderParagraphComponent(component, theme, themeName) {
5219
5397
  if (!isParagraphComponent(component)) return [];
5220
- const resolvedConfig = resolveParagraphProps(component.props, theme);
5398
+ const resolvedConfig = component.props;
5221
5399
  const listData = parseMarkdownList(resolvedConfig.text);
5222
5400
  if (listData) {
5223
5401
  const reference = globalNumberingRegistry.generateReference("markdown-list");
@@ -5283,6 +5461,7 @@ function renderParagraphComponent(component, theme, themeName) {
5283
5461
  fontSize: resolvedConfig.font?.size,
5284
5462
  fontColor: resolvedConfig.font?.color,
5285
5463
  bold: resolvedConfig.font?.bold,
5464
+ fontWeight: resolvedConfig.font?.fontWeight,
5286
5465
  italic: resolvedConfig.font?.italic,
5287
5466
  underline: resolvedConfig.font?.underline,
5288
5467
  // Pass outline level for TOC support
@@ -5402,7 +5581,7 @@ function fillMissingLevels(levels, maxLevel) {
5402
5581
  }
5403
5582
  function renderListComponent(component, theme, themeName) {
5404
5583
  if (!isListComponent(component)) return [];
5405
- const resolvedConfig = resolveListProps(component.props, theme);
5584
+ const resolvedConfig = component.props;
5406
5585
  const maxLevel = getMaxLevelFromItems(resolvedConfig.items);
5407
5586
  const reference = resolvedConfig.reference || globalNumberingRegistry.generateReference("list");
5408
5587
  if (!globalNumberingRegistry.has(reference)) {
@@ -5433,7 +5612,7 @@ function renderListComponent(component, theme, themeName) {
5433
5612
  // src/components/image.ts
5434
5613
  async function renderImageComponent(component, theme, themeName) {
5435
5614
  if (!isImageComponent(component)) return [];
5436
- const resolvedConfig = resolveImageProps(component.props, theme);
5615
+ const resolvedConfig = component.props;
5437
5616
  const imageSource = resolvedConfig.base64 || resolvedConfig.path;
5438
5617
  if (!imageSource) {
5439
5618
  throw new Error(
@@ -5793,7 +5972,6 @@ async function renderColumnsComponent(component, theme, themeName, context) {
5793
5972
  if (context.parent && isTextBoxComponent(context.parent)) {
5794
5973
  return await renderColumnsAsTable(component, theme, themeName, context);
5795
5974
  }
5796
- resolveColumnsProps(component.props, theme);
5797
5975
  const elements = [];
5798
5976
  if (component.children) {
5799
5977
  for (const child of component.children) {
@@ -5905,9 +6083,9 @@ async function renderColumnsAsTable(component, theme, themeName, context) {
5905
6083
  }
5906
6084
 
5907
6085
  // src/components/statistic.ts
5908
- function renderStatisticComponent(component, theme) {
6086
+ function renderStatisticComponent(component, _theme) {
5909
6087
  if (!isStatisticComponent(component)) return [];
5910
- const resolvedConfig = resolveStatisticProps(component.props, theme);
6088
+ const resolvedConfig = component.props;
5911
6089
  return createStatistic(
5912
6090
  {
5913
6091
  number: resolvedConfig.number,
@@ -6762,7 +6940,8 @@ function createBuilderImpl(state) {
6762
6940
  customThemes: state.customThemes,
6763
6941
  debug: state.debug,
6764
6942
  enableCache: state.enableCache,
6765
- services: state.services
6943
+ services: state.services,
6944
+ fonts: state.fonts
6766
6945
  };
6767
6946
  return createBuilderImpl(
6768
6947
  newState
@@ -6775,25 +6954,37 @@ function createBuilderImpl(state) {
6775
6954
  internalDocument,
6776
6955
  state.components
6777
6956
  );
6778
- const themeName = internalDocument.props.theme || "minimal";
6779
- const docTheme = resolveDocumentTheme(themeName);
6957
+ const baseThemeName = internalDocument.props.theme || "minimal";
6958
+ const docTheme = resolveDocumentTheme(baseThemeName);
6780
6959
  const warnings = [];
6960
+ const mode = applyExportMode({
6961
+ doc: internalDocument,
6962
+ theme: docTheme,
6963
+ fonts: state.fonts
6964
+ });
6965
+ const modedTheme = mode.theme;
6966
+ const themeName = scopedThemeName(baseThemeName, state.fonts?.mode);
6967
+ for (const w of mode.warnings) {
6968
+ warnings.push({
6969
+ component: "fontRegistry",
6970
+ message: w.message,
6971
+ severity: "warning",
6972
+ context: { code: w.code }
6973
+ });
6974
+ }
6781
6975
  const processedComponents = await processDocumentComponents(
6782
- internalDocument.children || [],
6976
+ mode.doc.children || [],
6783
6977
  warnings,
6784
- docTheme
6978
+ modedTheme
6785
6979
  );
6786
6980
  const processedDocument = {
6787
- ...internalDocument,
6981
+ ...mode.doc,
6788
6982
  children: processedComponents
6789
6983
  };
6790
- const [finalReportComponent] = normalizeDocument(processedDocument);
6791
- const structure = await processDocument(
6792
- finalReportComponent,
6793
- docTheme,
6794
- themeName
6795
- );
6796
- const layout = applyLayout(structure.sections, docTheme, themeName);
6984
+ const [modedDoc] = normalizeDocument(processedDocument);
6985
+ await resolveDocumentFonts(modedDoc, modedTheme, state.fonts, warnings);
6986
+ const structure = await processDocument(modedDoc, modedTheme, themeName);
6987
+ const layout = applyLayout(structure.sections, modedTheme, themeName);
6797
6988
  const generatedDocument = await renderDocument(structure, layout, {
6798
6989
  services: state.services
6799
6990
  });
@@ -6874,13 +7065,19 @@ function createBuilderImpl(state) {
6874
7065
  const themeName = internalDocument.props.theme || "minimal";
6875
7066
  const docTheme = resolveDocumentTheme(themeName);
6876
7067
  const warnings = [];
7068
+ const mode = applyExportMode({
7069
+ doc: internalDocument,
7070
+ theme: docTheme,
7071
+ fonts: state.fonts
7072
+ });
7073
+ const modedTheme = mode.theme;
6877
7074
  const processedComponents = await processDocumentComponents(
6878
- internalDocument.children || [],
7075
+ mode.doc.children || [],
6879
7076
  warnings,
6880
- docTheme
7077
+ modedTheme
6881
7078
  );
6882
7079
  const processedDocument = {
6883
- ...internalDocument,
7080
+ ...mode.doc,
6884
7081
  children: processedComponents
6885
7082
  };
6886
7083
  const [finalReportComponent] = normalizeDocument(processedDocument);
@@ -6912,7 +7109,8 @@ function createDocumentGenerator(options) {
6912
7109
  customThemes: options.customThemes,
6913
7110
  debug: options.debug ?? false,
6914
7111
  enableCache: options.enableCache ?? false,
6915
- services: options.services
7112
+ services: options.services,
7113
+ fonts: options.fonts
6916
7114
  };
6917
7115
  return createBuilderImpl(initialState);
6918
7116
  }