@json-to-office/core-docx 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +437 -6
- package/dist/index.js.map +1 -1
- package/dist/plugin/example/index.js +249 -4
- package/dist/plugin/example/index.js.map +1 -1
- package/dist/quality/facts.d.ts +56 -2
- package/dist/quality/facts.d.ts.map +1 -1
- package/dist/quality/rules.d.ts +16 -0
- package/dist/quality/rules.d.ts.map +1 -1
- package/dist/themes/defaults.d.ts +13 -1
- package/dist/themes/defaults.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -11,6 +11,7 @@ var __export = (target, all) => {
|
|
|
11
11
|
// src/themes/defaults.ts
|
|
12
12
|
function ensureThemeDefaults(theme) {
|
|
13
13
|
return {
|
|
14
|
+
...theme,
|
|
14
15
|
$schema: theme.$schema,
|
|
15
16
|
name: theme.name || "default",
|
|
16
17
|
displayName: theme.displayName || "Default Theme",
|
|
@@ -21,6 +22,7 @@ function ensureThemeDefaults(theme) {
|
|
|
21
22
|
...theme.colors || {}
|
|
22
23
|
},
|
|
23
24
|
fonts: {
|
|
25
|
+
...theme.fonts || {},
|
|
24
26
|
heading: { ...DEFAULT_FONTS.heading, ...theme.fonts?.heading || {} },
|
|
25
27
|
body: { ...DEFAULT_FONTS.body, ...theme.fonts?.body || {} },
|
|
26
28
|
mono: { ...DEFAULT_FONTS.mono, ...theme.fonts?.mono || {} },
|
|
@@ -11477,6 +11479,15 @@ function createRenderContext(document, theme, themeName) {
|
|
|
11477
11479
|
}
|
|
11478
11480
|
|
|
11479
11481
|
// src/quality/facts.ts
|
|
11482
|
+
import {
|
|
11483
|
+
chartEncodingFor,
|
|
11484
|
+
collectColorLiterals,
|
|
11485
|
+
collectFontFamilies,
|
|
11486
|
+
collectPlaceholders,
|
|
11487
|
+
hasUnitMarker,
|
|
11488
|
+
normalizeHex,
|
|
11489
|
+
normalizeHighchartsChart
|
|
11490
|
+
} from "@json-to-office/quality";
|
|
11480
11491
|
import { DEFAULT_DOCX_RENDERER_ID as DEFAULT_DOCX_RENDERER_ID2 } from "@json-to-office/shared-docx";
|
|
11481
11492
|
|
|
11482
11493
|
// src/json/normalizer.ts
|
|
@@ -11625,6 +11636,164 @@ function tableFact(props, path3, availableWidthTwips) {
|
|
|
11625
11636
|
explicitWidths
|
|
11626
11637
|
};
|
|
11627
11638
|
}
|
|
11639
|
+
var SERIES_COLOR_TOKENS = [
|
|
11640
|
+
"primary",
|
|
11641
|
+
"accent",
|
|
11642
|
+
"secondary",
|
|
11643
|
+
"accent4",
|
|
11644
|
+
"accent5",
|
|
11645
|
+
"accent6"
|
|
11646
|
+
];
|
|
11647
|
+
function chartFact(node, props, path3, paletteTokens) {
|
|
11648
|
+
const base = {
|
|
11649
|
+
id: `docx:chart:${path3}`,
|
|
11650
|
+
kind: "docx/chart",
|
|
11651
|
+
path: path3,
|
|
11652
|
+
componentName: typeof node.name === "string" ? node.name : "",
|
|
11653
|
+
paletteTokens
|
|
11654
|
+
};
|
|
11655
|
+
if (node.name === "highcharts") {
|
|
11656
|
+
const shape = normalizeHighchartsChart(props.options);
|
|
11657
|
+
return {
|
|
11658
|
+
...base,
|
|
11659
|
+
chartType: shape.chartType,
|
|
11660
|
+
encoding: shape.encoding,
|
|
11661
|
+
threeD: shape.threeD,
|
|
11662
|
+
seriesCount: shape.seriesCount,
|
|
11663
|
+
categoryCount: shape.categoryCount,
|
|
11664
|
+
seriesColorsStated: shape.seriesColorsStated,
|
|
11665
|
+
seriesColorsPath: `${path3}/props/options`,
|
|
11666
|
+
...shape.valueAxisMin !== void 0 && {
|
|
11667
|
+
valueAxisMin: shape.valueAxisMin
|
|
11668
|
+
},
|
|
11669
|
+
unitStated: shape.unitStated,
|
|
11670
|
+
annotation: {
|
|
11671
|
+
stated: shape.annotationStated,
|
|
11672
|
+
path: path3,
|
|
11673
|
+
slot: "props.options.caption.text"
|
|
11674
|
+
}
|
|
11675
|
+
};
|
|
11676
|
+
}
|
|
11677
|
+
const chartType = typeof props.type === "string" ? props.type : "";
|
|
11678
|
+
if (chartType === "") return void 0;
|
|
11679
|
+
const series = Array.isArray(props.data) ? props.data : [];
|
|
11680
|
+
const categoryCount = Math.max(
|
|
11681
|
+
0,
|
|
11682
|
+
...series.map((entry) => {
|
|
11683
|
+
const record = asRecord(entry);
|
|
11684
|
+
const labels = Array.isArray(record?.labels) ? record.labels.length : 0;
|
|
11685
|
+
const values = Array.isArray(record?.values) ? record.values.length : 0;
|
|
11686
|
+
return Math.max(labels, values);
|
|
11687
|
+
})
|
|
11688
|
+
);
|
|
11689
|
+
const caption = typeof props.caption === "string" ? props.caption : "";
|
|
11690
|
+
const title = typeof props.title === "string" ? props.title : "";
|
|
11691
|
+
return {
|
|
11692
|
+
...base,
|
|
11693
|
+
chartType,
|
|
11694
|
+
encoding: chartEncodingFor(chartType),
|
|
11695
|
+
// No 3D type exists in the DOCX chart schema — `office-open` draws none —
|
|
11696
|
+
// so this is always false here, and stays a field rather than an omission
|
|
11697
|
+
// so both formats answer the same questions.
|
|
11698
|
+
threeD: false,
|
|
11699
|
+
seriesCount: series.length,
|
|
11700
|
+
categoryCount,
|
|
11701
|
+
seriesColorsStated: Array.isArray(props.chartColors) && props.chartColors.length > 0,
|
|
11702
|
+
seriesColorsPath: `${path3}/props/chartColors`,
|
|
11703
|
+
// No `valueAxisMin`: a DOCX chart has no axis-floor property, so the
|
|
11704
|
+
// baseline is always the renderer's, which starts a bar at zero.
|
|
11705
|
+
unitStated: hasUnitMarker(
|
|
11706
|
+
typeof props.valAxisTitle === "string" ? props.valAxisTitle : ""
|
|
11707
|
+
) || hasUnitMarker(title) || hasUnitMarker(caption),
|
|
11708
|
+
annotation: {
|
|
11709
|
+
stated: caption.trim() !== "",
|
|
11710
|
+
path: path3,
|
|
11711
|
+
slot: "props.caption"
|
|
11712
|
+
}
|
|
11713
|
+
};
|
|
11714
|
+
}
|
|
11715
|
+
var DOCX_ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right", "justify"]);
|
|
11716
|
+
function resolvedCellText(content) {
|
|
11717
|
+
return typeof content === "string" ? content : "[component]";
|
|
11718
|
+
}
|
|
11719
|
+
function statesOwnBorders(authored) {
|
|
11720
|
+
if (!authored) return false;
|
|
11721
|
+
if (authored.borderSize !== void 0 || authored.borderColor !== void 0 || authored.hideBorders !== void 0) {
|
|
11722
|
+
return true;
|
|
11723
|
+
}
|
|
11724
|
+
const layers = [
|
|
11725
|
+
asRecord(authored.cellDefaults),
|
|
11726
|
+
asRecord(authored.headerCellDefaults)
|
|
11727
|
+
];
|
|
11728
|
+
if (layers.some(
|
|
11729
|
+
(layer) => layer?.borderSize !== void 0 || layer?.borderColor !== void 0
|
|
11730
|
+
)) {
|
|
11731
|
+
return true;
|
|
11732
|
+
}
|
|
11733
|
+
const columns = Array.isArray(authored.columns) ? authored.columns : [];
|
|
11734
|
+
return columns.some((column) => {
|
|
11735
|
+
const record = asRecord(column);
|
|
11736
|
+
const defaults = asRecord(record?.cellDefaults);
|
|
11737
|
+
return defaults?.borderSize !== void 0 || defaults?.borderColor !== void 0;
|
|
11738
|
+
});
|
|
11739
|
+
}
|
|
11740
|
+
function tableDesignFact(props, path3, theme, themeName, authored) {
|
|
11741
|
+
const authoredColumns = Array.isArray(props.columns) ? props.columns : [];
|
|
11742
|
+
if (authoredColumns.length === 0) return void 0;
|
|
11743
|
+
const model = resolveTableModel(
|
|
11744
|
+
props,
|
|
11745
|
+
theme,
|
|
11746
|
+
themeName
|
|
11747
|
+
);
|
|
11748
|
+
const bodyRows = model.rows;
|
|
11749
|
+
const drawnRows = bodyRows.length + (model.header ? 1 : 0);
|
|
11750
|
+
if (drawnRows === 0) return void 0;
|
|
11751
|
+
const fullGrid = statesOwnBorders(authored) && [...model.header ? [model.header] : [], ...bodyRows].every(
|
|
11752
|
+
(row) => row.cells.every(
|
|
11753
|
+
(cell) => ["top", "right", "bottom", "left"].every(
|
|
11754
|
+
(side) => !cell.borders[side].hidden && cell.borders[side].size > 0
|
|
11755
|
+
)
|
|
11756
|
+
)
|
|
11757
|
+
);
|
|
11758
|
+
const columns = authoredColumns.map(
|
|
11759
|
+
(column, index) => {
|
|
11760
|
+
const authoredColumn = asRecord(column) ?? {};
|
|
11761
|
+
const cells = Array.isArray(authoredColumn.cells) ? authoredColumn.cells : [];
|
|
11762
|
+
const alignments = /* @__PURE__ */ new Set();
|
|
11763
|
+
const values = [];
|
|
11764
|
+
bodyRows.forEach((row) => {
|
|
11765
|
+
const cell = row.cells[index];
|
|
11766
|
+
if (!cell || cell.missing) return;
|
|
11767
|
+
values.push(resolvedCellText(cell.content));
|
|
11768
|
+
alignments.add(cell.horizontalAlignment);
|
|
11769
|
+
});
|
|
11770
|
+
const header = model.header?.cells[index];
|
|
11771
|
+
return {
|
|
11772
|
+
index,
|
|
11773
|
+
path: `${path3}/props/columns/${index}`,
|
|
11774
|
+
...header?.content !== void 0 && {
|
|
11775
|
+
header: resolvedCellText(header.content)
|
|
11776
|
+
},
|
|
11777
|
+
values,
|
|
11778
|
+
alignment: alignments.size === 1 ? [...alignments][0] : alignments.size === 0 ? "left" : "mixed",
|
|
11779
|
+
hasCellDefaults: asRecord(authoredColumn.cellDefaults) !== void 0,
|
|
11780
|
+
hasHeader: asRecord(authoredColumn.header) !== void 0,
|
|
11781
|
+
cellsWithOwnAlignment: cells.flatMap((cell, cellIndex) => {
|
|
11782
|
+
const alignment2 = asRecord(cell)?.horizontalAlignment;
|
|
11783
|
+
return typeof alignment2 === "string" && DOCX_ALIGNMENTS.has(alignment2) && alignment2 !== "right" ? [cellIndex] : [];
|
|
11784
|
+
})
|
|
11785
|
+
};
|
|
11786
|
+
}
|
|
11787
|
+
);
|
|
11788
|
+
return {
|
|
11789
|
+
id: `docx:table-design:${path3}`,
|
|
11790
|
+
kind: "docx/table",
|
|
11791
|
+
path: path3,
|
|
11792
|
+
columns,
|
|
11793
|
+
rowCount: drawnRows,
|
|
11794
|
+
fullGrid
|
|
11795
|
+
};
|
|
11796
|
+
}
|
|
11628
11797
|
var TWIPS_PER_POINT3 = 20;
|
|
11629
11798
|
var SINGLE_LINE_RATIO = 1.2;
|
|
11630
11799
|
function finiteNumber(value) {
|
|
@@ -11653,20 +11822,23 @@ function characterSpacingPt(spacing) {
|
|
|
11653
11822
|
if (rule === void 0 || value === void 0) return 0;
|
|
11654
11823
|
return rule.type === "condensed" ? -value / TWIPS_PER_POINT3 : value / TWIPS_PER_POINT3;
|
|
11655
11824
|
}
|
|
11656
|
-
function
|
|
11825
|
+
function nodeAtPointer(root, pointer) {
|
|
11657
11826
|
let current = root;
|
|
11658
11827
|
for (const token of pointer.split("/").slice(1)) {
|
|
11659
11828
|
if (Array.isArray(current)) {
|
|
11660
11829
|
const index = Number(token);
|
|
11661
|
-
if (!Number.isInteger(index)) return
|
|
11830
|
+
if (!Number.isInteger(index)) return void 0;
|
|
11662
11831
|
current = current[index];
|
|
11663
11832
|
continue;
|
|
11664
11833
|
}
|
|
11665
11834
|
const record = asRecord(current);
|
|
11666
|
-
if (!record) return
|
|
11835
|
+
if (!record) return void 0;
|
|
11667
11836
|
current = record[token];
|
|
11668
11837
|
}
|
|
11669
|
-
return current
|
|
11838
|
+
return current;
|
|
11839
|
+
}
|
|
11840
|
+
function pointerExists(root, pointer) {
|
|
11841
|
+
return nodeAtPointer(root, pointer) !== void 0;
|
|
11670
11842
|
}
|
|
11671
11843
|
function styleKey(node, props) {
|
|
11672
11844
|
if (node.name === "heading") {
|
|
@@ -11847,6 +12019,67 @@ function prepareDocxQualityDocument(document, options = {}) {
|
|
|
11847
12019
|
...fact.relatedPaths && { relatedPaths: fact.relatedPaths }
|
|
11848
12020
|
};
|
|
11849
12021
|
};
|
|
12022
|
+
const paletteHexes = {};
|
|
12023
|
+
for (const [token, value] of Object.entries(
|
|
12024
|
+
resolved.theme.colors ?? {}
|
|
12025
|
+
)) {
|
|
12026
|
+
if (typeof value !== "string") continue;
|
|
12027
|
+
const hex = normalizeHex(value);
|
|
12028
|
+
if (hex) paletteHexes[token] = hex;
|
|
12029
|
+
}
|
|
12030
|
+
const paletteTokens = SERIES_COLOR_TOKENS.filter(
|
|
12031
|
+
(token) => paletteHexes[token] !== void 0
|
|
12032
|
+
);
|
|
12033
|
+
const authoredPropsAt = (pointer) => asRecord(asRecord(nodeAtPointer(context.document, pointer))?.props);
|
|
12034
|
+
addFact({
|
|
12035
|
+
id: "docx:theme",
|
|
12036
|
+
kind: "docx/theme",
|
|
12037
|
+
path: "/props",
|
|
12038
|
+
themeName: context.themeName,
|
|
12039
|
+
paletteHexes,
|
|
12040
|
+
// `heading` and `body` only. A theme also names `mono` and `light`, but
|
|
12041
|
+
// those paint nothing until a component asks for them — counting an
|
|
12042
|
+
// unused `Courier New` against a document's family budget would flag a
|
|
12043
|
+
// report that only ever uses one typeface.
|
|
12044
|
+
fontFamilies: [
|
|
12045
|
+
...new Set(
|
|
12046
|
+
["heading", "body"].flatMap((role) => {
|
|
12047
|
+
const family = asRecord(
|
|
12048
|
+
resolved.theme.fonts?.[role]
|
|
12049
|
+
)?.family;
|
|
12050
|
+
return typeof family === "string" && family.trim() !== "" ? [family] : [];
|
|
12051
|
+
})
|
|
12052
|
+
)
|
|
12053
|
+
]
|
|
12054
|
+
});
|
|
12055
|
+
collectColorLiterals(document).forEach((literal, index) => {
|
|
12056
|
+
addFact({
|
|
12057
|
+
id: `docx:color:${index}:${literal.path}`,
|
|
12058
|
+
kind: "docx/color",
|
|
12059
|
+
path: literal.path,
|
|
12060
|
+
raw: literal.raw,
|
|
12061
|
+
hex: literal.hex
|
|
12062
|
+
});
|
|
12063
|
+
});
|
|
12064
|
+
collectFontFamilies(document).forEach((use, index) => {
|
|
12065
|
+
addFact({
|
|
12066
|
+
id: `docx:font:${index}:${use.path}`,
|
|
12067
|
+
kind: "docx/font-family",
|
|
12068
|
+
path: use.path,
|
|
12069
|
+
family: use.family
|
|
12070
|
+
});
|
|
12071
|
+
});
|
|
12072
|
+
collectPlaceholders(document).forEach((occurrence, index) => {
|
|
12073
|
+
addFact({
|
|
12074
|
+
id: `docx:placeholder:${index}:${occurrence.path}`,
|
|
12075
|
+
kind: "docx/placeholder",
|
|
12076
|
+
path: occurrence.path,
|
|
12077
|
+
text: occurrence.text,
|
|
12078
|
+
placeholderKind: occurrence.match.kind,
|
|
12079
|
+
pattern: occurrence.match.pattern,
|
|
12080
|
+
excerpt: occurrence.match.excerpt
|
|
12081
|
+
});
|
|
12082
|
+
});
|
|
11850
12083
|
let previousVisitPath;
|
|
11851
12084
|
let lastFrame;
|
|
11852
12085
|
let flowIndex = 0;
|
|
@@ -11856,6 +12089,18 @@ function prepareDocxQualityDocument(document, options = {}) {
|
|
|
11856
12089
|
if (node.name === "table") {
|
|
11857
12090
|
const fact = tableFact(props, path3, page.availableWidthTwips);
|
|
11858
12091
|
if (fact) addFact(fact);
|
|
12092
|
+
const design = tableDesignFact(
|
|
12093
|
+
props,
|
|
12094
|
+
path3,
|
|
12095
|
+
resolved.theme,
|
|
12096
|
+
context.themeName,
|
|
12097
|
+
authoredPropsAt(path3)
|
|
12098
|
+
);
|
|
12099
|
+
if (design) addFact(design);
|
|
12100
|
+
}
|
|
12101
|
+
if (node.name === "chart" || node.name === "highcharts") {
|
|
12102
|
+
const fact = chartFact(node, props, path3, paletteTokens);
|
|
12103
|
+
if (fact) addFact(fact);
|
|
11859
12104
|
}
|
|
11860
12105
|
if (node.name === "paragraph" || node.name === "text-box") {
|
|
11861
12106
|
const floating = asRecord(props.floating);
|