@json-to-office/jto-ops 4.2.0 → 4.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.d.ts +140 -2
- package/dist/index.js +518 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1746,7 +1746,7 @@ var LINE_OR_WORD_PATTERN = /<line\s+xMin="(-?[\d.]+)"\s+yMin="(-?[\d.]+)"\s+xMax
|
|
|
1746
1746
|
function parsePdfTextBbox(bboxXml) {
|
|
1747
1747
|
const pages = [];
|
|
1748
1748
|
for (const pageMatch of bboxXml.matchAll(PAGE_PATTERN)) {
|
|
1749
|
-
const
|
|
1749
|
+
const words2 = [];
|
|
1750
1750
|
const lines = [];
|
|
1751
1751
|
let open;
|
|
1752
1752
|
for (const token of pageMatch[3].matchAll(LINE_OR_WORD_PATTERN)) {
|
|
@@ -1762,8 +1762,8 @@ function parsePdfTextBbox(bboxXml) {
|
|
|
1762
1762
|
words: []
|
|
1763
1763
|
};
|
|
1764
1764
|
} else {
|
|
1765
|
-
open?.words.push(
|
|
1766
|
-
|
|
1765
|
+
open?.words.push(words2.length);
|
|
1766
|
+
words2.push({
|
|
1767
1767
|
xMin: Number(token[5]),
|
|
1768
1768
|
yMin: Number(token[6]),
|
|
1769
1769
|
xMax: Number(token[7]),
|
|
@@ -1775,7 +1775,7 @@ function parsePdfTextBbox(bboxXml) {
|
|
|
1775
1775
|
pages.push({
|
|
1776
1776
|
widthPt: Number(pageMatch[1]),
|
|
1777
1777
|
heightPt: Number(pageMatch[2]),
|
|
1778
|
-
words,
|
|
1778
|
+
words: words2,
|
|
1779
1779
|
lines
|
|
1780
1780
|
});
|
|
1781
1781
|
}
|
|
@@ -2052,17 +2052,108 @@ function indexDocument(pages, exclude = /* @__PURE__ */ new Set()) {
|
|
|
2052
2052
|
const positions = [];
|
|
2053
2053
|
let cursor = 0;
|
|
2054
2054
|
pages.forEach((page, pageIndex) => {
|
|
2055
|
-
page.words
|
|
2055
|
+
for (const index of readingOrder(page.words)) {
|
|
2056
|
+
const word = page.words[index];
|
|
2056
2057
|
const key = `${pageIndex}:${index}`;
|
|
2057
2058
|
const fragment = exclude.has(key) ? "" : normalizeForMatch(word.text);
|
|
2058
2059
|
refs.push({ pageIndex, word: index });
|
|
2059
2060
|
fragments.push(fragment);
|
|
2060
2061
|
positions.push(cursor);
|
|
2061
2062
|
cursor += fragment.length;
|
|
2062
|
-
}
|
|
2063
|
+
}
|
|
2063
2064
|
});
|
|
2064
2065
|
return { pages, refs, fragments, positions, stream: fragments.join("") };
|
|
2065
2066
|
}
|
|
2067
|
+
function readingOrder(words2) {
|
|
2068
|
+
const rotated = new Set(
|
|
2069
|
+
words2.map((_, i) => i).filter((i) => {
|
|
2070
|
+
const w = words2[i];
|
|
2071
|
+
return w.text.length > 2 && w.yMax - w.yMin > 1.5 * (w.xMax - w.xMin);
|
|
2072
|
+
})
|
|
2073
|
+
);
|
|
2074
|
+
const upright = uprightOrder(
|
|
2075
|
+
words2,
|
|
2076
|
+
words2.map((_, i) => i).filter((i) => !rotated.has(i))
|
|
2077
|
+
);
|
|
2078
|
+
const order = [];
|
|
2079
|
+
let next = 0;
|
|
2080
|
+
for (let i = 0; i < words2.length; i++) {
|
|
2081
|
+
if (rotated.has(i)) order.push(i);
|
|
2082
|
+
else order.push(upright[next++]);
|
|
2083
|
+
}
|
|
2084
|
+
return order;
|
|
2085
|
+
}
|
|
2086
|
+
function uprightOrder(words2, indices) {
|
|
2087
|
+
const rows = [];
|
|
2088
|
+
const byY = [...indices].sort(
|
|
2089
|
+
(a, b) => words2[a].yMin - words2[b].yMin || words2[a].xMin - words2[b].xMin
|
|
2090
|
+
);
|
|
2091
|
+
for (const i of byY) {
|
|
2092
|
+
const row = rows[rows.length - 1];
|
|
2093
|
+
if (row && sameRow(words2[row[0]], words2[i])) row.push(i);
|
|
2094
|
+
else rows.push([i]);
|
|
2095
|
+
}
|
|
2096
|
+
const fragmentsOf = (row) => {
|
|
2097
|
+
const sorted = [...row].sort((a, b) => words2[a].xMin - words2[b].xMin);
|
|
2098
|
+
const out = [];
|
|
2099
|
+
for (const i of sorted) {
|
|
2100
|
+
const last = out[out.length - 1];
|
|
2101
|
+
const w = words2[i];
|
|
2102
|
+
const height = w.yMax - w.yMin;
|
|
2103
|
+
if (last && w.xMin - last.xMax <= height / 2) {
|
|
2104
|
+
last.words.push(i);
|
|
2105
|
+
last.xMax = Math.max(last.xMax, w.xMax);
|
|
2106
|
+
} else out.push({ xMin: w.xMin, xMax: w.xMax, words: [i] });
|
|
2107
|
+
}
|
|
2108
|
+
return out;
|
|
2109
|
+
};
|
|
2110
|
+
const fragments = rows.map(fragmentsOf);
|
|
2111
|
+
const boxes = rows.map((row) => ({
|
|
2112
|
+
yMin: Math.min(...row.map((i) => words2[i].yMin)),
|
|
2113
|
+
yMax: Math.max(...row.map((i) => words2[i].yMax))
|
|
2114
|
+
}));
|
|
2115
|
+
const order = [];
|
|
2116
|
+
let cluster = null;
|
|
2117
|
+
const flush = () => {
|
|
2118
|
+
if (!cluster) return;
|
|
2119
|
+
if (!cluster.multi) {
|
|
2120
|
+
for (const r of cluster.rows)
|
|
2121
|
+
for (const f of fragments[r]) order.push(...f.words);
|
|
2122
|
+
} else {
|
|
2123
|
+
cluster.columns.sort((a, b) => a.xMin - b.xMin);
|
|
2124
|
+
for (const column of cluster.columns) {
|
|
2125
|
+
column.fragments.sort(
|
|
2126
|
+
(a, b) => words2[a.words[0]].yMin - words2[b.words[0]].yMin || a.xMin - b.xMin
|
|
2127
|
+
);
|
|
2128
|
+
for (const f of column.fragments) order.push(...f.words);
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
cluster = null;
|
|
2132
|
+
};
|
|
2133
|
+
const overlapping = (columns, f) => columns.filter(
|
|
2134
|
+
(c) => Math.min(c.xMax, f.xMax) - Math.max(c.xMin, f.xMin) > 0
|
|
2135
|
+
);
|
|
2136
|
+
for (let r = 0; r < rows.length; r++) {
|
|
2137
|
+
const lineHeight = boxes[r].yMax - boxes[r].yMin;
|
|
2138
|
+
const adjacent = cluster !== null && boxes[r].yMin - boxes[r - 1].yMax <= lineHeight * 0.9;
|
|
2139
|
+
const wide = cluster !== null && fragments[r].some((f) => overlapping(cluster.columns, f).length > 1);
|
|
2140
|
+
if (!adjacent || wide) flush();
|
|
2141
|
+
if (!cluster) cluster = { rows: [], columns: [], multi: false };
|
|
2142
|
+
cluster.rows.push(r);
|
|
2143
|
+
if (fragments[r].length > 1) cluster.multi = true;
|
|
2144
|
+
for (const f of fragments[r]) {
|
|
2145
|
+
const hit = overlapping(cluster.columns, f)[0];
|
|
2146
|
+
if (hit) {
|
|
2147
|
+
hit.xMin = Math.min(hit.xMin, f.xMin);
|
|
2148
|
+
hit.xMax = Math.max(hit.xMax, f.xMax);
|
|
2149
|
+
hit.fragments.push(f);
|
|
2150
|
+
} else
|
|
2151
|
+
cluster.columns.push({ xMin: f.xMin, xMax: f.xMax, fragments: [f] });
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
flush();
|
|
2155
|
+
return order;
|
|
2156
|
+
}
|
|
2066
2157
|
function fragmentAt(index, offset) {
|
|
2067
2158
|
let low = 0;
|
|
2068
2159
|
let high = index.positions.length - 1;
|
|
@@ -2483,14 +2574,14 @@ function draftRenderedFindings(input) {
|
|
|
2483
2574
|
pages.forEach((page, pageIndex) => {
|
|
2484
2575
|
const lines = lineOfWord(page);
|
|
2485
2576
|
const seen = /* @__PURE__ */ new Set();
|
|
2486
|
-
const
|
|
2487
|
-
const byTop =
|
|
2577
|
+
const words2 = page.words;
|
|
2578
|
+
const byTop = words2.map((_, i) => i).sort((a, b) => words2[a].yMin - words2[b].yMin);
|
|
2488
2579
|
for (let p = 0; p < byTop.length; p++) {
|
|
2489
2580
|
const i = byTop[p];
|
|
2490
|
-
const a =
|
|
2581
|
+
const a = words2[i];
|
|
2491
2582
|
for (let q = p + 1; q < byTop.length; q++) {
|
|
2492
2583
|
const j = byTop[q];
|
|
2493
|
-
const b =
|
|
2584
|
+
const b = words2[j];
|
|
2494
2585
|
if (b.yMin >= a.yMax) break;
|
|
2495
2586
|
const shared = intersects(a, b);
|
|
2496
2587
|
if (shared <= 0) continue;
|
|
@@ -2724,9 +2815,416 @@ function analyzeRenderedDocument(input, options = {}) {
|
|
|
2724
2815
|
analysis
|
|
2725
2816
|
};
|
|
2726
2817
|
}
|
|
2818
|
+
|
|
2819
|
+
// src/block-matrix.ts
|
|
2820
|
+
import {
|
|
2821
|
+
blockDependencies,
|
|
2822
|
+
readBlockDefinitions
|
|
2823
|
+
} from "@json-to-office/shared";
|
|
2824
|
+
var FALLBACK_FONTS = {
|
|
2825
|
+
heading: { family: "DejaVu Sans" },
|
|
2826
|
+
body: { family: "DejaVu Sans" }
|
|
2827
|
+
};
|
|
2828
|
+
var MATRIX_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAABytg0kAAAAFElEQVR42mNk+M9QzwAFjDAGACPuA/8fMSCgAAAAAElFTkSuQmCC";
|
|
2829
|
+
var WORDS = [
|
|
2830
|
+
"performance",
|
|
2831
|
+
"delivery",
|
|
2832
|
+
"retention",
|
|
2833
|
+
"operating",
|
|
2834
|
+
"programme",
|
|
2835
|
+
"contracted",
|
|
2836
|
+
"recommendation",
|
|
2837
|
+
"quarter",
|
|
2838
|
+
"measurement",
|
|
2839
|
+
"ownership",
|
|
2840
|
+
"renewal",
|
|
2841
|
+
"segment",
|
|
2842
|
+
"margin",
|
|
2843
|
+
"baseline",
|
|
2844
|
+
"procurement",
|
|
2845
|
+
"integration"
|
|
2846
|
+
];
|
|
2847
|
+
function words(count, seed, sentence) {
|
|
2848
|
+
if (count <= 0) return "";
|
|
2849
|
+
const out = [`Item${seed}`];
|
|
2850
|
+
for (let i = 1; i < count; i += 1) out.push(WORDS[(seed + i) % WORDS.length]);
|
|
2851
|
+
return sentence ? `${out.join(" ")}.` : out.join(" ");
|
|
2852
|
+
}
|
|
2853
|
+
function widestNumber(length, seed = 0) {
|
|
2854
|
+
if (length <= 0) return "";
|
|
2855
|
+
if (length === 1) return "9";
|
|
2856
|
+
if (length < 4) return `\u2212${"9".repeat(length - 1)}`;
|
|
2857
|
+
const group = (digits2) => Array.from({ length: digits2 }, (_, i) => String((i + seed) % 9 + 1)).join("").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
2858
|
+
let digits = 1;
|
|
2859
|
+
while (group(digits + 1).length + 3 <= length) digits += 1;
|
|
2860
|
+
const integer = group(digits);
|
|
2861
|
+
return `\u2212${integer}.${"0".repeat(length - integer.length - 2)}`;
|
|
2862
|
+
}
|
|
2863
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2864
|
+
var NUMERIC_SLOTS = /* @__PURE__ */ new Set(["value", "delta", "cells"]);
|
|
2865
|
+
function stringValue(slot, name, edge, seed) {
|
|
2866
|
+
if (slot.enum && slot.enum.length > 0) {
|
|
2867
|
+
const values = slot.enum.map(String);
|
|
2868
|
+
return edge === "max" ? values.reduce((a, b) => b.length > a.length ? b : a) : values[0];
|
|
2869
|
+
}
|
|
2870
|
+
if (slot.maxLength !== void 0 && NUMERIC_SLOTS.has(name)) {
|
|
2871
|
+
return widestNumber(
|
|
2872
|
+
edge === "max" ? slot.maxLength : Math.min(3, slot.maxLength),
|
|
2873
|
+
seed
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
if (slot.maxLength !== void 0 && name === "unit") {
|
|
2877
|
+
return edge === "max" ? ` \u20AC${"m".repeat(Math.max(0, slot.maxLength - 2))}`.slice(
|
|
2878
|
+
0,
|
|
2879
|
+
slot.maxLength
|
|
2880
|
+
) : "%";
|
|
2881
|
+
}
|
|
2882
|
+
if (slot.maxLength !== void 0) {
|
|
2883
|
+
const target = edge === "max" ? slot.maxLength : Math.max(1, slot.minLength ?? 1);
|
|
2884
|
+
let text = `Item${seed}`.slice(0, target);
|
|
2885
|
+
let i = 0;
|
|
2886
|
+
for (; ; ) {
|
|
2887
|
+
const next = WORDS[(seed + i) % WORDS.length];
|
|
2888
|
+
i += 1;
|
|
2889
|
+
if (text.length + 1 + next.length > target) {
|
|
2890
|
+
if (i > WORDS.length) break;
|
|
2891
|
+
continue;
|
|
2892
|
+
}
|
|
2893
|
+
text += ` ${next}`;
|
|
2894
|
+
}
|
|
2895
|
+
return text;
|
|
2896
|
+
}
|
|
2897
|
+
const budget = slot.maxWords ?? 12;
|
|
2898
|
+
const count = edge === "max" ? budget : Math.max(1, Math.min(2, budget));
|
|
2899
|
+
const sentence = !slot.oneLine && slot.role !== "source";
|
|
2900
|
+
if (slot.role === "source") return `Source: ${words(count - 1, seed, false)}`;
|
|
2901
|
+
if (name === "number")
|
|
2902
|
+
return edge === "max" ? words(count, seed, false) : "01";
|
|
2903
|
+
return words(count, seed, sentence);
|
|
2904
|
+
}
|
|
2905
|
+
function componentValue(name, edge, seed) {
|
|
2906
|
+
if (name === "chart") {
|
|
2907
|
+
const categories = edge === "max" ? 8 : 2;
|
|
2908
|
+
const series = edge === "max" ? 3 : 1;
|
|
2909
|
+
return {
|
|
2910
|
+
name: "chart",
|
|
2911
|
+
props: {
|
|
2912
|
+
type: "column",
|
|
2913
|
+
valAxisTitle: edge === "max" ? `${words(3, seed, false)} (\u20ACm)` : "Revenue (\u20ACm)",
|
|
2914
|
+
...edge === "max" && { catAxisTitle: words(3, seed + 3, false) },
|
|
2915
|
+
chartColors: ["primary", "secondary", "accent"].slice(0, series),
|
|
2916
|
+
data: Array.from({ length: series }, (_, s) => ({
|
|
2917
|
+
name: words(edge === "max" ? 3 : 1, seed + s, false),
|
|
2918
|
+
// Two words per category is where LibreOffice's native chart
|
|
2919
|
+
// still keeps rotated labels clear of the axis title; at three
|
|
2920
|
+
// the title is drawn over them, at any category count.
|
|
2921
|
+
labels: Array.from(
|
|
2922
|
+
{ length: categories },
|
|
2923
|
+
(_2, c) => edge === "max" ? words(2, seed + c, false) : `Q${c + 1}`
|
|
2924
|
+
),
|
|
2925
|
+
values: Array.from(
|
|
2926
|
+
{ length: categories },
|
|
2927
|
+
(_2, c) => 1.2 * (c + 1) + s
|
|
2928
|
+
)
|
|
2929
|
+
}))
|
|
2930
|
+
}
|
|
2931
|
+
};
|
|
2932
|
+
}
|
|
2933
|
+
return {
|
|
2934
|
+
name: "image",
|
|
2935
|
+
props: {
|
|
2936
|
+
base64: MATRIX_IMAGE,
|
|
2937
|
+
width: edge === "max" ? name === "logo" ? "40%" : "100%" : "25%",
|
|
2938
|
+
alt: words(6, seed, false)
|
|
2939
|
+
}
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
function boundarySlotValue(slot, name, edge, seed = 0) {
|
|
2943
|
+
switch (slot.type) {
|
|
2944
|
+
case "string":
|
|
2945
|
+
return stringValue(slot, name, edge, seed);
|
|
2946
|
+
case "number":
|
|
2947
|
+
case "integer":
|
|
2948
|
+
return edge === "max" ? slot.maximum ?? 9999 : slot.minimum ?? 0;
|
|
2949
|
+
case "boolean":
|
|
2950
|
+
return edge === "max" ? true : slot.default ?? true;
|
|
2951
|
+
case "component":
|
|
2952
|
+
return componentValue(name, edge, seed);
|
|
2953
|
+
case "object": {
|
|
2954
|
+
const out = {};
|
|
2955
|
+
for (const [key, property] of Object.entries(slot.properties ?? {})) {
|
|
2956
|
+
if (edge === "min" && !property.required) continue;
|
|
2957
|
+
const value = boundarySlotValue(property, key, edge, seed + key.length);
|
|
2958
|
+
if (value !== void 0) out[key] = value;
|
|
2959
|
+
}
|
|
2960
|
+
return out;
|
|
2961
|
+
}
|
|
2962
|
+
case "array": {
|
|
2963
|
+
const count = edge === "max" ? slot.maxItems ?? 3 : slot.minItems ?? (slot.required ? 1 : 0);
|
|
2964
|
+
const item = slot.items ?? { type: "string" };
|
|
2965
|
+
return Array.from(
|
|
2966
|
+
{ length: count },
|
|
2967
|
+
(_, i) => boundarySlotValue(item, name, edge, seed + i)
|
|
2968
|
+
);
|
|
2969
|
+
}
|
|
2970
|
+
default:
|
|
2971
|
+
return void 0;
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
function boundaryInvocation(name, definition, edge, seed = 0, requiredRoles = []) {
|
|
2975
|
+
const slots = {};
|
|
2976
|
+
let offset = seed;
|
|
2977
|
+
for (const [slotName, slot] of Object.entries(definition.slots)) {
|
|
2978
|
+
offset += slotName.length;
|
|
2979
|
+
const required = slot.required === true || slot.role !== void 0 && requiredRoles.includes(slot.role);
|
|
2980
|
+
if (edge === "min" && !required) continue;
|
|
2981
|
+
const value = boundarySlotValue(slot, slotName, edge, offset);
|
|
2982
|
+
if (value !== void 0) slots[slotName] = value;
|
|
2983
|
+
}
|
|
2984
|
+
const labels = slots.labels;
|
|
2985
|
+
const columns = slots.columns;
|
|
2986
|
+
if (Array.isArray(labels) && Array.isArray(columns)) {
|
|
2987
|
+
const cells = definition.slots.columns?.items?.properties?.cells;
|
|
2988
|
+
for (const column of columns) {
|
|
2989
|
+
if (isRecord(column) && Array.isArray(column.cells) && cells?.items) {
|
|
2990
|
+
column.cells = labels.map(
|
|
2991
|
+
(_, i) => boundarySlotValue(cells.items, "cells", edge, offset + i)
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
return { name: "block", props: { ref: name, slots } };
|
|
2997
|
+
}
|
|
2998
|
+
function overBudgetInvocation(name, definition, seed = 0) {
|
|
2999
|
+
const invocation = boundaryInvocation(name, definition, "max", seed);
|
|
3000
|
+
const slots = invocation.props.slots;
|
|
3001
|
+
for (const [slotName, slot] of Object.entries(definition.slots)) {
|
|
3002
|
+
if (slot.type === "string" && slot.maxWords !== void 0) {
|
|
3003
|
+
slots[slotName] = `${words(slot.maxWords + 1, seed, !slot.oneLine)}`;
|
|
3004
|
+
return { invocation, slot: slotName, kind: "words" };
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
for (const [slotName, slot] of Object.entries(definition.slots)) {
|
|
3008
|
+
if (slot.type === "array" && slot.maxItems !== void 0 && Array.isArray(slots[slotName])) {
|
|
3009
|
+
const items = slots[slotName];
|
|
3010
|
+
slots[slotName] = [...items, items[0]];
|
|
3011
|
+
return { invocation, slot: slotName, kind: "items" };
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
return void 0;
|
|
3015
|
+
}
|
|
3016
|
+
function enumerateBlockDefinitions(template, templateName) {
|
|
3017
|
+
const definitions = readBlockDefinitions(template);
|
|
3018
|
+
const examples = /* @__PURE__ */ new Map();
|
|
3019
|
+
const walk = (node) => {
|
|
3020
|
+
if (Array.isArray(node)) {
|
|
3021
|
+
node.forEach(walk);
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3024
|
+
if (!isRecord(node)) return;
|
|
3025
|
+
const props = node.props;
|
|
3026
|
+
if (node.name === "block" && isRecord(props) && typeof props.ref === "string" && !examples.has(props.ref)) {
|
|
3027
|
+
examples.set(props.ref, isRecord(props.slots) ? props.slots : {});
|
|
3028
|
+
}
|
|
3029
|
+
walk(node.children);
|
|
3030
|
+
};
|
|
3031
|
+
walk(isRecord(template) ? template.children : void 0);
|
|
3032
|
+
return Object.entries(definitions).map(([name, definition]) => ({
|
|
3033
|
+
name,
|
|
3034
|
+
template: templateName,
|
|
3035
|
+
pointer: `/props/blocks/${name.replace(/~/g, "~0").replace(/\//g, "~1")}`,
|
|
3036
|
+
definition,
|
|
3037
|
+
...examples.has(name) && { example: examples.get(name) }
|
|
3038
|
+
}));
|
|
3039
|
+
}
|
|
3040
|
+
var CHROME = ["cover", "running-head", "section-opener"];
|
|
3041
|
+
function nominal(entry, name, definitions) {
|
|
3042
|
+
if (!entry) return void 0;
|
|
3043
|
+
if (entry.example) {
|
|
3044
|
+
return {
|
|
3045
|
+
name: "block",
|
|
3046
|
+
props: { ref: name, slots: structuredClone(entry.example) }
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
return boundaryInvocation(name, definitions[name], "min");
|
|
3050
|
+
}
|
|
3051
|
+
function inlineImages(value) {
|
|
3052
|
+
if (Array.isArray(value)) return value.map(inlineImages);
|
|
3053
|
+
if (!isRecord(value)) return value;
|
|
3054
|
+
if (value.name === "image" && isRecord(value.props) && typeof value.props.path === "string") {
|
|
3055
|
+
const props = { ...value.props, base64: MATRIX_IMAGE };
|
|
3056
|
+
delete props.path;
|
|
3057
|
+
return { ...value, props };
|
|
3058
|
+
}
|
|
3059
|
+
if (value.name === "highcharts") return componentValue("chart", "min", 0);
|
|
3060
|
+
return Object.fromEntries(
|
|
3061
|
+
Object.entries(value).map(([k, v]) => [k, inlineImages(v)])
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
function withDefinitions(definitions, names) {
|
|
3065
|
+
const out = {};
|
|
3066
|
+
for (const name of names) {
|
|
3067
|
+
if (!(name in definitions)) continue;
|
|
3068
|
+
out[name] = definitions[name];
|
|
3069
|
+
for (const dependency of blockDependencies(definitions, name)) {
|
|
3070
|
+
if (dependency in definitions) out[dependency] = definitions[dependency];
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
return out;
|
|
3074
|
+
}
|
|
3075
|
+
function conditions(props, children, theme, font, canvas) {
|
|
3076
|
+
const sections = children.map((child) => {
|
|
3077
|
+
if (!isRecord(child) || child.name !== "section") return child;
|
|
3078
|
+
const props2 = isRecord(child.props) ? child.props : {};
|
|
3079
|
+
const page = isRecord(props2.page) ? props2.page : {};
|
|
3080
|
+
return { ...child, props: { ...props2, page: { ...page, size: canvas } } };
|
|
3081
|
+
});
|
|
3082
|
+
const chart = JSON.stringify(sections).includes('"name":"chart"');
|
|
3083
|
+
return {
|
|
3084
|
+
name: "docx",
|
|
3085
|
+
...chart && { renderer: "office-open" },
|
|
3086
|
+
props: {
|
|
3087
|
+
...props,
|
|
3088
|
+
theme,
|
|
3089
|
+
...font === "fallback" && { themeOverrides: { fonts: FALLBACK_FONTS } }
|
|
3090
|
+
},
|
|
3091
|
+
children: sections
|
|
3092
|
+
};
|
|
3093
|
+
}
|
|
3094
|
+
var BODY_COPY = words(40, 1e3, true);
|
|
3095
|
+
function blockCaseDocument(entries, name, edge, theme, { font = "design", canvas = "A4", requiredRoles = [] } = {}) {
|
|
3096
|
+
const definitions = Object.fromEntries(
|
|
3097
|
+
entries.map((e) => [e.name, e.definition])
|
|
3098
|
+
);
|
|
3099
|
+
const byName = new Map(entries.map((e) => [e.name, e]));
|
|
3100
|
+
const under = (chrome) => chrome === name ? boundaryInvocation(name, definitions[name], edge, 0, requiredRoles) : inlineImages(nominal(byName.get(chrome), chrome, definitions));
|
|
3101
|
+
const cover = under("cover");
|
|
3102
|
+
const runningHead = under("running-head");
|
|
3103
|
+
const opener = under("section-opener");
|
|
3104
|
+
const body = [];
|
|
3105
|
+
if (!CHROME.includes(name)) {
|
|
3106
|
+
if (name === "footnotes" && byName.has("figure")) {
|
|
3107
|
+
body.push(
|
|
3108
|
+
inlineImages(nominal(byName.get("figure"), "figure", definitions))
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3111
|
+
body.push(
|
|
3112
|
+
boundaryInvocation(name, definitions[name], edge, 0, requiredRoles)
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3115
|
+
body.push({ name: "paragraph", props: { text: BODY_COPY } });
|
|
3116
|
+
const used = /* @__PURE__ */ new Set([
|
|
3117
|
+
name,
|
|
3118
|
+
...CHROME,
|
|
3119
|
+
...name === "footnotes" ? ["figure"] : []
|
|
3120
|
+
]);
|
|
3121
|
+
const children = [];
|
|
3122
|
+
if (cover) children.push({ name: "section", children: [cover] });
|
|
3123
|
+
children.push({
|
|
3124
|
+
name: "section",
|
|
3125
|
+
children: [runningHead, opener, ...body].filter((c) => c !== void 0)
|
|
3126
|
+
});
|
|
3127
|
+
return conditions(
|
|
3128
|
+
{
|
|
3129
|
+
metadata: {
|
|
3130
|
+
title: "Boundary report",
|
|
3131
|
+
author: "jto-ops block matrix",
|
|
3132
|
+
company: "Example client",
|
|
3133
|
+
date: "September 2026"
|
|
3134
|
+
},
|
|
3135
|
+
blocks: withDefinitions(definitions, used)
|
|
3136
|
+
},
|
|
3137
|
+
children,
|
|
3138
|
+
theme,
|
|
3139
|
+
font,
|
|
3140
|
+
canvas
|
|
3141
|
+
);
|
|
3142
|
+
}
|
|
3143
|
+
function reportCaseDocument(template, edge, theme, { font = "design", canvas = "A4", requiredRoles = [] } = {}) {
|
|
3144
|
+
if (!isRecord(template) || !isRecord(template.props))
|
|
3145
|
+
throw new Error("A template is a document with props.");
|
|
3146
|
+
const definitions = readBlockDefinitions(template);
|
|
3147
|
+
let seed = 0;
|
|
3148
|
+
const rewrite = (node) => {
|
|
3149
|
+
if (Array.isArray(node)) return node.map(rewrite);
|
|
3150
|
+
if (!isRecord(node)) return node;
|
|
3151
|
+
const props2 = node.props;
|
|
3152
|
+
if (node.name === "block" && isRecord(props2) && typeof props2.ref === "string" && definitions[props2.ref]) {
|
|
3153
|
+
seed += 7;
|
|
3154
|
+
return boundaryInvocation(
|
|
3155
|
+
props2.ref,
|
|
3156
|
+
definitions[props2.ref],
|
|
3157
|
+
edge,
|
|
3158
|
+
seed,
|
|
3159
|
+
requiredRoles
|
|
3160
|
+
);
|
|
3161
|
+
}
|
|
3162
|
+
if (node.name === "paragraph" && isRecord(props2) && typeof props2.text === "string") {
|
|
3163
|
+
return {
|
|
3164
|
+
...node,
|
|
3165
|
+
props: { ...props2, text: edge === "max" ? BODY_COPY : props2.text }
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
return Array.isArray(node.children) ? { ...node, children: rewrite(node.children) } : node;
|
|
3169
|
+
};
|
|
3170
|
+
const props = structuredClone(template.props);
|
|
3171
|
+
delete props.theme;
|
|
3172
|
+
delete props.themeOverrides;
|
|
3173
|
+
return conditions(
|
|
3174
|
+
props,
|
|
3175
|
+
rewrite(template.children),
|
|
3176
|
+
theme,
|
|
3177
|
+
font,
|
|
3178
|
+
canvas
|
|
3179
|
+
);
|
|
3180
|
+
}
|
|
3181
|
+
function generateBlockMatrix(template, templateName, options) {
|
|
3182
|
+
const entries = enumerateBlockDefinitions(template, templateName);
|
|
3183
|
+
const fonts = options.fonts ?? ["design", "fallback"];
|
|
3184
|
+
const edges = options.edges ?? ["min", "max"];
|
|
3185
|
+
const canvases = options.canvases ?? ["A4"];
|
|
3186
|
+
const cases = [];
|
|
3187
|
+
for (const theme of options.themes)
|
|
3188
|
+
for (const font of fonts)
|
|
3189
|
+
for (const canvas of canvases)
|
|
3190
|
+
for (const edge of edges) {
|
|
3191
|
+
if (options.blocks !== false)
|
|
3192
|
+
for (const entry of entries)
|
|
3193
|
+
cases.push({
|
|
3194
|
+
id: `${entry.name}@${edge}/${theme}/${font}/${canvas}`,
|
|
3195
|
+
block: entry.name,
|
|
3196
|
+
edge,
|
|
3197
|
+
theme,
|
|
3198
|
+
font,
|
|
3199
|
+
canvas,
|
|
3200
|
+
document: blockCaseDocument(entries, entry.name, edge, theme, {
|
|
3201
|
+
font,
|
|
3202
|
+
canvas,
|
|
3203
|
+
requiredRoles: options.requiredRoles
|
|
3204
|
+
})
|
|
3205
|
+
});
|
|
3206
|
+
if (options.report !== false)
|
|
3207
|
+
cases.push({
|
|
3208
|
+
id: `report@${edge}/${theme}/${font}/${canvas}`,
|
|
3209
|
+
block: "report",
|
|
3210
|
+
edge,
|
|
3211
|
+
theme,
|
|
3212
|
+
font,
|
|
3213
|
+
canvas,
|
|
3214
|
+
document: reportCaseDocument(template, edge, theme, {
|
|
3215
|
+
font,
|
|
3216
|
+
canvas,
|
|
3217
|
+
requiredRoles: options.requiredRoles
|
|
3218
|
+
})
|
|
3219
|
+
});
|
|
3220
|
+
}
|
|
3221
|
+
return cases;
|
|
3222
|
+
}
|
|
2727
3223
|
export {
|
|
2728
3224
|
DocxFormatAdapter,
|
|
3225
|
+
FALLBACK_FONTS,
|
|
2729
3226
|
FontconfigStager,
|
|
3227
|
+
MATRIX_IMAGE,
|
|
2730
3228
|
MacOSCoreTextStager,
|
|
2731
3229
|
NoopFontStager,
|
|
2732
3230
|
PptxFormatAdapter,
|
|
@@ -2736,22 +3234,31 @@ export {
|
|
|
2736
3234
|
analyzeRenderedDocument,
|
|
2737
3235
|
assignInventory,
|
|
2738
3236
|
authoredTextForMatch,
|
|
3237
|
+
blockCaseDocument,
|
|
3238
|
+
boundaryInvocation,
|
|
3239
|
+
boundarySlotValue,
|
|
2739
3240
|
clearRasterizerCache,
|
|
2740
3241
|
createAdapter,
|
|
2741
3242
|
createLibreOfficePptxBatchRasterizer,
|
|
2742
3243
|
createLibreOfficePptxRasterizer,
|
|
2743
3244
|
emitDiagnostic,
|
|
3245
|
+
enumerateBlockDefinitions,
|
|
2744
3246
|
extractPdfFonts,
|
|
2745
3247
|
extractPdfTextGeometry,
|
|
2746
3248
|
familyRendered,
|
|
3249
|
+
generateBlockMatrix,
|
|
2747
3250
|
getFontStager,
|
|
2748
3251
|
getRasterizerCacheStats,
|
|
2749
3252
|
normalizeForMatch,
|
|
3253
|
+
overBudgetInvocation,
|
|
2750
3254
|
parsePdfFonts,
|
|
2751
3255
|
parsePdfTextBbox,
|
|
2752
3256
|
pdffontsAvailable,
|
|
2753
3257
|
pdftotextAvailable,
|
|
3258
|
+
readingOrder,
|
|
3259
|
+
reportCaseDocument,
|
|
2754
3260
|
runWithDiagnosticSink,
|
|
2755
|
-
stderrDiagnosticSink
|
|
3261
|
+
stderrDiagnosticSink,
|
|
3262
|
+
widestNumber
|
|
2756
3263
|
};
|
|
2757
3264
|
//# sourceMappingURL=index.js.map
|