@json-to-office/core-docx 3.3.0 → 4.0.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.
@@ -193,6 +193,7 @@ var init_minimal_docx_theme = __esm({
193
193
  name: "minimal",
194
194
  displayName: "Minimal Clean",
195
195
  description: "A quiet, warm minimal theme \u2014 Calibri throughout, sage-green ink on ivory neutrals, a large tracked-tight bold title, wide margins and hairline rules",
196
+ whenToUse: "Quiet documents where the type carries the tone: notes, briefs, drafts and internal papers that want warmth without decoration.",
196
197
  version: "4.0.0",
197
198
  colors: {
198
199
  primary: "#2B302B",
@@ -382,6 +383,7 @@ var init_devportal_docx_theme = __esm({
382
383
  name: "devportal",
383
384
  displayName: "Field Editorial",
384
385
  description: "A compact editorial theme \u2014 Helvetica in near-black ink with a burnt-orange accent, tight condensed titles, letterspaced labels, warm tinted fills and hairline rules",
386
+ whenToUse: "Developer and product documentation, API guides and technical handbooks: dense text, code and tables that need compact, letterspaced structure.",
385
387
  version: "4.0.0",
386
388
  colors: {
387
389
  primary: "#12191F",
@@ -580,6 +582,7 @@ var init_vermilion_docx_theme = __esm({
580
582
  name: "vermilion",
581
583
  displayName: "Vermilion Editorial",
582
584
  description: "Poster-red editorial system \u2014 vermilion display headings, ink text, warm creams and hairline rules",
585
+ whenToUse: "Editorial and annual-report work that wants a strong red display voice: covers, feature-style sections, reports built around a few large headings.",
583
586
  version: "1.0.0",
584
587
  colors: {
585
588
  primary: "#282829",
@@ -798,6 +801,7 @@ var init_consulting_docx_theme = __esm({
798
801
  name: "consulting",
799
802
  displayName: "Consulting House",
800
803
  description: "The house style for client and technical reports \u2014 near-black ink, three greys and one deep-blue accent, Calibri body under Arial headings, hairline rules, no fills behind body text, safe fonts only",
804
+ whenToUse: "Client and technical reports, memos and anything a client will read as the house's own work; the default choice for a report unless the brief names a brand.",
801
805
  version: "1.0.0",
802
806
  colors: {
803
807
  primary: "#1A1F26",
@@ -12132,8 +12136,10 @@ import {
12132
12136
  } from "@json-to-office/quality";
12133
12137
  import { DEFAULT_DOCX_RENDERER_ID as DEFAULT_DOCX_RENDERER_ID2 } from "@json-to-office/shared-docx";
12134
12138
  import {
12139
+ designCanvas as designCanvas2,
12135
12140
  designColors as designColors2,
12136
- resolveDesignColor as resolveDesignColor2
12141
+ resolveDesignColor as resolveDesignColor2,
12142
+ typeScaleSizes
12137
12143
  } from "@json-to-office/shared";
12138
12144
 
12139
12145
  // src/json/normalizer.ts
@@ -12903,6 +12909,93 @@ function walkActive(node, path3, page, visit) {
12903
12909
  (child, index) => walkActive(child, `${path3}/children/${index}`, page, visit)
12904
12910
  );
12905
12911
  }
12912
+ function textSizeFact(node, props, path3, typography, role) {
12913
+ const size = effectiveFontSize(node, props, typography);
12914
+ if (size === void 0) return void 0;
12915
+ return {
12916
+ id: `docx:text-size:${path3}`,
12917
+ kind: "docx/text-size",
12918
+ path: path3,
12919
+ role: role ?? styleKey(node, props),
12920
+ fontSizePt: size.fontSizePt,
12921
+ authored: size.authored,
12922
+ ...size.authored && { sizePath: `${path3}/props/font/size` }
12923
+ };
12924
+ }
12925
+ function tableTextSizeFacts(props, authored, path3, typography, page) {
12926
+ const facts = [];
12927
+ const sizeOf = (holder) => finiteNumber(asRecord(asRecord(holder)?.font)?.size);
12928
+ const authoredSize = (holder, pointer, role) => {
12929
+ const size = sizeOf(holder);
12930
+ if (size === void 0) return;
12931
+ facts.push({
12932
+ id: `docx:text-size:${pointer}`,
12933
+ kind: "docx/text-size",
12934
+ path: pointer,
12935
+ role,
12936
+ fontSizePt: size,
12937
+ authored: true,
12938
+ sizePath: `${pointer}/font/size`
12939
+ });
12940
+ };
12941
+ const cellContent = (holder, pointer, role) => {
12942
+ const content = asRecord(holder)?.content;
12943
+ if (asRecord(content) === void 0) return;
12944
+ walkActive(content, `${pointer}/content`, page, (node, nodePath) => {
12945
+ if (node.name !== "paragraph" && node.name !== "heading") return;
12946
+ const nodeProps = asRecord(node.props) ?? {};
12947
+ if (typeof nodeProps.text !== "string" || nodeProps.text.trim() === "")
12948
+ return;
12949
+ const fact = textSizeFact(node, nodeProps, nodePath, typography, role);
12950
+ if (fact) facts.push(fact);
12951
+ });
12952
+ };
12953
+ if (authored) {
12954
+ authoredSize(
12955
+ authored.cellDefaults,
12956
+ `${path3}/props/cellDefaults`,
12957
+ "tableCell"
12958
+ );
12959
+ authoredSize(
12960
+ authored.headerCellDefaults,
12961
+ `${path3}/props/headerCellDefaults`,
12962
+ "tableHeader"
12963
+ );
12964
+ const columns = Array.isArray(authored.columns) ? authored.columns : [];
12965
+ columns.forEach((column, index) => {
12966
+ const record = asRecord(column) ?? {};
12967
+ const columnPath = `${path3}/props/columns/${index}`;
12968
+ authoredSize(
12969
+ record.cellDefaults,
12970
+ `${columnPath}/cellDefaults`,
12971
+ "tableCell"
12972
+ );
12973
+ authoredSize(record.header, `${columnPath}/header`, "tableHeader");
12974
+ cellContent(record.header, `${columnPath}/header`, "tableHeader");
12975
+ const cells = Array.isArray(record.cells) ? record.cells : [];
12976
+ cells.forEach((cell, cellIndex) => {
12977
+ authoredSize(cell, `${columnPath}/cells/${cellIndex}`, "tableCell");
12978
+ cellContent(cell, `${columnPath}/cells/${cellIndex}`, "tableCell");
12979
+ });
12980
+ });
12981
+ }
12982
+ for (const [key, role] of [
12983
+ ["cellDefaults", "tableCell"],
12984
+ ["headerCellDefaults", "tableHeader"]
12985
+ ]) {
12986
+ const size = sizeOf(props[key]);
12987
+ if (size === void 0) continue;
12988
+ facts.push({
12989
+ id: `docx:text-size:${path3}:${role}`,
12990
+ kind: "docx/text-size",
12991
+ path: path3,
12992
+ role,
12993
+ fontSizePt: size,
12994
+ authored: false
12995
+ });
12996
+ }
12997
+ return facts;
12998
+ }
12906
12999
  function prepareDocxQualityDocument(document, options = {}) {
12907
13000
  const themed = options.context ?? resolveThemeContext(normalizeDocument(document)[0], {
12908
13001
  customThemes: options.customThemes,
@@ -12972,6 +13065,43 @@ function prepareDocxQualityDocument(document, options = {}) {
12972
13065
  const header = part("header");
12973
13066
  const footer = part("footer");
12974
13067
  inherited = { header, footer };
13068
+ for (const kind of ["header", "footer"]) {
13069
+ if (!Array.isArray(props[kind])) continue;
13070
+ const drawnByBlock = !Array.isArray(
13071
+ asRecord(
13072
+ asRecord(
13073
+ (Array.isArray(themed.document.children) ? themed.document.children : [])[index]
13074
+ )?.props
13075
+ )?.[kind]
13076
+ );
13077
+ const part2 = props[kind];
13078
+ const visitChrome = (child, childPath) => {
13079
+ if (child.name !== "paragraph" && child.name !== "heading") return;
13080
+ const childProps = asRecord(child.props) ?? {};
13081
+ if (typeof childProps.text !== "string" || childProps.text.trim() === "")
13082
+ return;
13083
+ const fact = textSizeFact(
13084
+ child,
13085
+ childProps,
13086
+ childPath,
13087
+ typography,
13088
+ kind
13089
+ );
13090
+ if (fact)
13091
+ addFact({
13092
+ ...fact,
13093
+ generated: drawnByBlock || authoredPath(childPath) !== childPath
13094
+ });
13095
+ };
13096
+ part2.forEach(
13097
+ (child, childIndex) => walkActive(
13098
+ child,
13099
+ `/children/${index}/props/${kind}/${childIndex}`,
13100
+ basePage,
13101
+ visitChrome
13102
+ )
13103
+ );
13104
+ }
12975
13105
  addFact({
12976
13106
  id: `docx:section-chrome:${index}`,
12977
13107
  kind: "docx/section-chrome",
@@ -13005,12 +13135,34 @@ function prepareDocxQualityDocument(document, options = {}) {
13005
13135
  (value) => `#${resolveDesignColor2(value, visualColors)}`
13006
13136
  ) ?? SERIES_COLOR_TOKENS.filter((token) => paletteHexes[token] !== void 0);
13007
13137
  const authoredPropsAt = (pointer) => asRecord(asRecord(nodeAtPointer(context.document, pointer))?.props);
13138
+ const roleSizesPt = {};
13139
+ for (const key of Object.keys(typography.styles)) {
13140
+ const size = effectiveFontSize(
13141
+ { name: "paragraph" },
13142
+ { themeStyle: key },
13143
+ typography
13144
+ );
13145
+ if (size) roleSizesPt[key] = size.fontSizePt;
13146
+ }
13147
+ const scale = resolved.theme.typography?.scale?.[designCanvas2("docx", resolved.theme.page?.size)];
13148
+ const typeScalePt = [
13149
+ .../* @__PURE__ */ new Set([
13150
+ ...Object.values(roleSizesPt),
13151
+ ...["heading", "body", "mono", "light"].flatMap((role) => {
13152
+ const size = resolveFontSize(resolved.theme, role);
13153
+ return size === void 0 ? [] : [size];
13154
+ }),
13155
+ ...scale ? typeScaleSizes(scale) : []
13156
+ ])
13157
+ ].sort((a, b) => a - b);
13008
13158
  addFact({
13009
13159
  id: "docx:theme",
13010
13160
  kind: "docx/theme",
13011
13161
  path: "/props",
13012
13162
  themeName: context.themeName,
13013
13163
  paletteHexes,
13164
+ typeScalePt,
13165
+ roleSizesPt,
13014
13166
  // `heading` and `body` only. A theme also names `mono` and `light`, but
13015
13167
  // those paint nothing until a component asks for them — counting an
13016
13168
  // unused `Courier New` against a document's family budget would flag a
@@ -13063,6 +13215,15 @@ function prepareDocxQualityDocument(document, options = {}) {
13063
13215
  if (node.name === "table") {
13064
13216
  const fact = tableFact(props, path3, page.availableWidthTwips);
13065
13217
  if (fact) addFact(fact);
13218
+ for (const size of tableTextSizeFacts(
13219
+ props,
13220
+ authoredPropsAt(path3),
13221
+ path3,
13222
+ typography,
13223
+ page
13224
+ )) {
13225
+ addFact({ ...size, generated: authoredPath(size.path) !== size.path });
13226
+ }
13066
13227
  const design = tableDesignFact(
13067
13228
  props,
13068
13229
  path3,
@@ -13121,6 +13282,10 @@ function prepareDocxQualityDocument(document, options = {}) {
13121
13282
  if (node.name === "paragraph" || node.name === "heading") {
13122
13283
  const fact = lineBoxFact(node, props, path3, typography, context.document);
13123
13284
  if (fact) addFact(fact);
13285
+ if (typeof props.text === "string" && props.text.trim() !== "") {
13286
+ const fact2 = textSizeFact(node, props, path3, typography);
13287
+ if (fact2) addFact({ ...fact2, generated: authoredPath(path3) !== path3 });
13288
+ }
13124
13289
  }
13125
13290
  if (node.name === "image" || node.name === "visual") {
13126
13291
  for (const fact of svgTextFacts(props, path3)) addFact(fact);