@reportwright/viewer 0.12.0 → 0.12.1
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/CHANGELOG.md +55 -1
- package/README.md +27 -5
- package/dist/{chunk-KJTRZJHN.js → chunk-2BPBQMH2.js} +4 -3
- package/dist/chunk-2BPBQMH2.js.map +6 -0
- package/dist/{chunk-ODREGGKA.js → chunk-3D2KFKG2.js} +5 -4
- package/dist/chunk-3D2KFKG2.js.map +6 -0
- package/dist/chunk-6TPZ7G74.js +494 -0
- package/dist/chunk-6TPZ7G74.js.map +6 -0
- package/dist/chunk-BRB34AJ2.js +16 -0
- package/dist/{chunk-APRPSQOD.js → chunk-DPVJEFKP.js} +28 -9
- package/dist/chunk-DPVJEFKP.js.map +6 -0
- package/dist/{chunk-RYHJUQSV.js → chunk-DXHLIJEY.js} +2 -2
- package/dist/{chunk-SR2TQ2ZK.js → chunk-F6RMRXIN.js} +2 -2
- package/dist/chunk-F6RMRXIN.js.map +6 -0
- package/dist/{chunk-3D43XGIN.js → chunk-M6AYND5G.js} +8 -638
- package/dist/chunk-M6AYND5G.js.map +6 -0
- package/dist/{chunk-EOVGKHAS.js → chunk-NR2G4TMG.js} +59 -51
- package/dist/chunk-NR2G4TMG.js.map +6 -0
- package/dist/{chunk-5N6OFCHJ.js → chunk-PGP3QA6L.js} +22 -10
- package/dist/chunk-PGP3QA6L.js.map +6 -0
- package/dist/{chunk-FARG6X2U.js → chunk-Q6MMPKQA.js} +14 -9
- package/dist/chunk-Q6MMPKQA.js.map +6 -0
- package/dist/{chunk-SBD55ZES.js → chunk-S2IUUI6N.js} +76 -32
- package/dist/chunk-S2IUUI6N.js.map +6 -0
- package/dist/chunk-ULKEEARQ.js +553 -0
- package/dist/chunk-ULKEEARQ.js.map +6 -0
- package/dist/{chunk-M6GENFAH.js → chunk-VNOWPSE6.js} +527 -1461
- package/dist/chunk-VNOWPSE6.js.map +6 -0
- package/dist/chunk-Z7MKH66Z.js +642 -0
- package/dist/chunk-Z7MKH66Z.js.map +6 -0
- package/dist/designer.d.ts +10 -3
- package/dist/designer.js +41 -31
- package/dist/designer.js.map +2 -2
- package/dist/engine.worker.js +71 -0
- package/dist/engine.worker.js.map +6 -0
- package/dist/peer/chunk-2FZTYEDE.js +15 -0
- package/dist/peer/{chunk-NPYKZINU.js → chunk-6I2EAAVN.js} +39 -31
- package/dist/peer/chunk-6I2EAAVN.js.map +6 -0
- package/dist/peer/{chunk-IPU2YRL2.js → chunk-BMAXIBP2.js} +28 -9
- package/dist/peer/chunk-BMAXIBP2.js.map +6 -0
- package/dist/peer/chunk-BQFI3DHK.js +494 -0
- package/dist/peer/chunk-BQFI3DHK.js.map +6 -0
- package/dist/peer/{chunk-76ZABVTI.js → chunk-R4VX5UC7.js} +4 -3
- package/dist/peer/{chunk-KBT6ZGGO.js → chunk-TSC4RAH3.js} +22 -10
- package/dist/peer/chunk-TSC4RAH3.js.map +6 -0
- package/dist/peer/{chunk-M4AGFUWE.js → chunk-V42KN7HD.js} +2 -2
- package/dist/peer/{chunk-Y5CAHLXS.js → chunk-WQGENQZO.js} +5 -4
- package/dist/peer/{chunk-MCJPN2KL.js → chunk-WSNRD2AQ.js} +14 -9
- package/dist/peer/chunk-WSNRD2AQ.js.map +6 -0
- package/dist/peer/designer.js +41 -31
- package/dist/peer/designer.js.map +2 -2
- package/dist/peer/engine.worker.js +70 -0
- package/dist/peer/engine.worker.js.map +6 -0
- package/dist/peer/viewer.js +12 -5
- package/dist/peer/viewer.js.map +1 -1
- package/dist/viewer.d.ts +9 -1
- package/dist/viewer.js +12 -5
- package/dist/viewer.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3D43XGIN.js.map +0 -6
- package/dist/chunk-5N6OFCHJ.js.map +0 -6
- package/dist/chunk-AH47FS6Y.js +0 -13
- package/dist/chunk-APRPSQOD.js.map +0 -6
- package/dist/chunk-EOVGKHAS.js.map +0 -6
- package/dist/chunk-FARG6X2U.js.map +0 -6
- package/dist/chunk-M6GENFAH.js.map +0 -6
- package/dist/chunk-SBD55ZES.js.map +0 -6
- package/dist/chunk-SR2TQ2ZK.js.map +0 -6
- package/dist/peer/chunk-76ZABVTI.js.map +0 -6
- package/dist/peer/chunk-GBITRNHE.js +0 -14806
- package/dist/peer/chunk-GBITRNHE.js.map +0 -6
- package/dist/peer/chunk-IMQRHDR4.js +0 -12
- package/dist/peer/chunk-IPU2YRL2.js.map +0 -6
- package/dist/peer/chunk-KBT6ZGGO.js.map +0 -6
- package/dist/peer/chunk-MCJPN2KL.js.map +0 -6
- package/dist/peer/chunk-NPYKZINU.js.map +0 -6
- package/dist/peer/chunk-Y5CAHLXS.js.map +0 -6
- /package/dist/{chunk-AH47FS6Y.js.map → chunk-BRB34AJ2.js.map} +0 -0
- /package/dist/{chunk-RYHJUQSV.js.map → chunk-DXHLIJEY.js.map} +0 -0
- /package/dist/peer/{chunk-IMQRHDR4.js.map → chunk-2FZTYEDE.js.map} +0 -0
- /package/dist/{chunk-KJTRZJHN.js.map → peer/chunk-R4VX5UC7.js.map} +0 -0
- /package/dist/peer/{chunk-M4AGFUWE.js.map → chunk-V42KN7HD.js.map} +0 -0
- /package/dist/{chunk-ODREGGKA.js.map → peer/chunk-WQGENQZO.js.map} +0 -0
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TextMeasurer,
|
|
3
|
+
finishText
|
|
4
|
+
} from "./chunk-ULKEEARQ.js";
|
|
5
|
+
import {
|
|
6
|
+
safeUrl
|
|
7
|
+
} from "./chunk-72S6DETS.js";
|
|
1
8
|
import {
|
|
2
9
|
ExprError,
|
|
3
10
|
capText,
|
|
@@ -19,115 +26,392 @@ import {
|
|
|
19
26
|
validTimeZone
|
|
20
27
|
} from "./chunk-GJS242RR.js";
|
|
21
28
|
import {
|
|
22
|
-
safeUrl
|
|
23
|
-
} from "./chunk-72S6DETS.js";
|
|
24
|
-
import {
|
|
25
|
-
ALL_FONT_KEYS,
|
|
26
29
|
CORE_FONT_KEY,
|
|
27
|
-
DEFAULT_FAMILY,
|
|
28
30
|
FontStore,
|
|
29
31
|
OPTIONAL_KEYS,
|
|
30
32
|
PRELOAD_KEYS,
|
|
31
33
|
PX,
|
|
32
34
|
SHAPER,
|
|
33
|
-
fallbackKey,
|
|
34
|
-
fontCss,
|
|
35
|
-
fontFamilyCss,
|
|
36
|
-
fontFile,
|
|
37
|
-
fontKeysOf,
|
|
38
35
|
fontsFor,
|
|
39
36
|
fontsForDef,
|
|
40
37
|
imageInfo,
|
|
41
38
|
resolveFontKey,
|
|
42
|
-
resolveImageSrc
|
|
43
|
-
|
|
44
|
-
} from "./chunk-3D43XGIN.js";
|
|
39
|
+
resolveImageSrc
|
|
40
|
+
} from "./chunk-Z7MKH66Z.js";
|
|
45
41
|
|
|
46
|
-
// src/
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
// src/engine/data/guard.js
|
|
43
|
+
var FETCH_DEFAULTS = { timeoutMs: 3e4, maxBytes: 64 * 1048576, hops: 5 };
|
|
44
|
+
var V4 = [
|
|
45
|
+
["0.0.0.0", 8],
|
|
46
|
+
["10.0.0.0", 8],
|
|
47
|
+
["100.64.0.0", 10],
|
|
48
|
+
["127.0.0.0", 8],
|
|
49
|
+
["169.254.0.0", 16],
|
|
50
|
+
["172.16.0.0", 12],
|
|
51
|
+
["192.0.0.0", 24],
|
|
52
|
+
["192.0.2.0", 24],
|
|
53
|
+
["192.168.0.0", 16],
|
|
54
|
+
["198.18.0.0", 15],
|
|
55
|
+
["198.51.100.0", 24],
|
|
56
|
+
["203.0.113.0", 24],
|
|
57
|
+
["224.0.0.0", 3]
|
|
58
|
+
].map(([a, p]) => [v4num(String(a)), Number(p)]);
|
|
59
|
+
function v4num(ip) {
|
|
60
|
+
return ip.split(".").reduce((n, x) => n * 256 + Number(x), 0);
|
|
61
|
+
}
|
|
62
|
+
function privateV4(ip) {
|
|
63
|
+
const n = v4num(ip);
|
|
64
|
+
return V4.some(([a, p]) => Math.floor(n / 2 ** (32 - p)) === Math.floor(a / 2 ** (32 - p)));
|
|
65
|
+
}
|
|
66
|
+
function groups6(ip) {
|
|
67
|
+
const [h, t = ""] = ip.split("::");
|
|
68
|
+
const a = h ? h.split(":") : [], b = t ? t.split(":") : [];
|
|
69
|
+
if (b.length && b[b.length - 1].includes(".")) {
|
|
70
|
+
const q = b.pop().split(".").map(Number);
|
|
71
|
+
b.push((q[0] << 8 | q[1]).toString(16), (q[2] << 8 | q[3]).toString(16));
|
|
72
|
+
}
|
|
73
|
+
return [...a, ...Array(Math.max(0, 8 - a.length - b.length)).fill("0"), ...b].map((x) => parseInt(x, 16) || 0);
|
|
74
|
+
}
|
|
75
|
+
function privateV6(ip) {
|
|
76
|
+
const g = groups6(ip);
|
|
77
|
+
const v4 = () => `${g[6] >> 8}.${g[6] & 255}.${g[7] >> 8}.${g[7] & 255}`;
|
|
78
|
+
if (g.slice(0, 6).every((x) => x === 0)) return true;
|
|
79
|
+
if (g.slice(0, 5).every((x) => x === 0) && g[5] === 65535) return privateV4(v4());
|
|
80
|
+
if (g[0] === 100 && g[1] === 65435) return g[2] === 1 || privateV4(v4());
|
|
81
|
+
if (g[0] === 8194 || g[0] === 8193 && g[1] === 0) return true;
|
|
82
|
+
if ((g[0] & 65024) === 64512 || (g[0] & 65472) === 65152 || (g[0] & 65472) === 65216 || (g[0] & 65280) === 65280) return true;
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
function privateHost(host) {
|
|
86
|
+
const h = host.replace(/^\[|\]$/g, "").toLowerCase();
|
|
87
|
+
if (h.includes(":")) return privateV6(h);
|
|
88
|
+
if (/^\d+\.\d+\.\d+\.\d+$/.test(h)) return privateV4(h);
|
|
89
|
+
return h === "localhost" || h.endsWith(".localhost") || h.endsWith(".local") || h.endsWith(".internal");
|
|
90
|
+
}
|
|
91
|
+
var refuse = (host) => Object.assign(new Error(`"${host}" is a private or local address; a data source may not reach it (pass allowHosts: ["${host}"] to render to allow it)`), { status: 403, code: "EPRIVATE" });
|
|
92
|
+
function nodeMod(name) {
|
|
93
|
+
const p = (
|
|
94
|
+
/** @type {any} */
|
|
95
|
+
globalThis.process
|
|
96
|
+
);
|
|
97
|
+
if (!p?.versions?.node) return null;
|
|
98
|
+
if (!p.getBuiltinModule) throw Object.assign(new Error("The engine's fetch guard needs Node 20.16 or later (process.getBuiltinModule); or pass your own fetch to render"), { status: 500 });
|
|
99
|
+
return p.getBuiltinModule(name);
|
|
100
|
+
}
|
|
101
|
+
var inNode = () => !!/** @type {any} */
|
|
102
|
+
globalThis.process?.versions?.node;
|
|
103
|
+
var addrHost = (a) => a.family === 6 || String(a.address).includes(":") ? `[${a.address}]` : String(a.address);
|
|
104
|
+
function checkUrl(input, allow) {
|
|
105
|
+
let u;
|
|
49
106
|
try {
|
|
50
|
-
|
|
107
|
+
u = new URL(input);
|
|
51
108
|
} catch {
|
|
52
|
-
|
|
109
|
+
throw Object.assign(new Error(`Not a URL: ${String(input).slice(0, 200)}`), { status: 400 });
|
|
53
110
|
}
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
111
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") throw Object.assign(new Error(`Only http and https URLs can be fetched, not ${u.protocol}`), { status: 400 });
|
|
112
|
+
if (u.username || u.password) throw Object.assign(new Error("A user name or password in a data URL is not allowed"), { status: 400 });
|
|
113
|
+
const host = u.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
114
|
+
if (!host) throw Object.assign(new Error("A data URL needs a host"), { status: 400 });
|
|
115
|
+
if (allow.includes(host)) return u;
|
|
116
|
+
let bad = true;
|
|
117
|
+
try {
|
|
118
|
+
bad = privateHost(u.hostname);
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
if (bad) throw refuse(host);
|
|
122
|
+
return u;
|
|
58
123
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
124
|
+
function nodeRequest(url, init, trusted) {
|
|
125
|
+
const http = nodeMod("node:http"), https = nodeMod("node:https"), dns = nodeMod("node:dns"), stream = nodeMod("node:stream");
|
|
126
|
+
const lookup = (name, o, cb) => dns.lookup(name, { ...o, all: true }, (err, list) => {
|
|
127
|
+
if (err || !Array.isArray(list) || !list.length) return cb(err || refuse(name));
|
|
128
|
+
let bad = true;
|
|
129
|
+
try {
|
|
130
|
+
bad = !trusted && list.some((a) => privateHost(addrHost(a)));
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
if (bad) return cb(refuse(name));
|
|
134
|
+
return o.all ? cb(null, list) : cb(null, list[0].address, list[0].family);
|
|
135
|
+
});
|
|
136
|
+
const headers = Object.fromEntries(new Headers(init.headers || {}));
|
|
137
|
+
headers["accept-encoding"] = "identity";
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const req = (url.protocol === "https:" ? https : http).request(url, { method: init.method || "GET", headers, lookup, signal: init.signal }, (res) => {
|
|
140
|
+
const pairs = [];
|
|
141
|
+
for (let i = 0; i < res.rawHeaders.length; i += 2) pairs.push([res.rawHeaders[i], res.rawHeaders[i + 1]]);
|
|
142
|
+
const none = [101, 204, 205, 304].includes(res.statusCode) || (init.method || "GET").toUpperCase() === "HEAD";
|
|
143
|
+
if (none) res.resume();
|
|
144
|
+
const enc = String(res.headers["content-encoding"] || "").trim().toLowerCase();
|
|
145
|
+
let body = res, decoded = false;
|
|
146
|
+
if (!none && enc && enc !== "identity") {
|
|
147
|
+
const zlib = nodeMod("node:zlib");
|
|
148
|
+
const dec2 = enc === "gzip" || enc === "x-gzip" ? zlib.createGunzip() : enc === "deflate" ? zlib.createInflate() : enc === "br" ? zlib.createBrotliDecompress() : null;
|
|
149
|
+
if (!dec2) {
|
|
150
|
+
res.resume();
|
|
151
|
+
return reject(Object.assign(new Error(`the response is encoded as "${enc}", which the data fetch cannot decode`), { status: 415 }));
|
|
152
|
+
}
|
|
153
|
+
body = stream.pipeline(res, dec2, () => {
|
|
154
|
+
});
|
|
155
|
+
decoded = true;
|
|
156
|
+
}
|
|
157
|
+
const kept = decoded ? pairs.filter(([k]) => !/^(content-encoding|content-length)$/i.test(k)) : pairs;
|
|
158
|
+
resolve(new Response(none ? null : stream.Readable.toWeb(body), { status: res.statusCode, statusText: res.statusMessage, headers: (
|
|
159
|
+
/** @type {any} */
|
|
160
|
+
kept
|
|
161
|
+
) }));
|
|
162
|
+
});
|
|
163
|
+
req.on("error", reject);
|
|
164
|
+
req.end(init.body ?? void 0);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
var CROSS_ORIGIN_KEEP = /* @__PURE__ */ new Set(["accept", "accept-language", "content-type"]);
|
|
168
|
+
function redirectRequest(from, to, req, status) {
|
|
169
|
+
if (from.protocol === "https:" && to.protocol !== "https:") throw Object.assign(new Error(`${from.host} redirects from https to http; not followed`), { status: 403 });
|
|
170
|
+
const headers = new Headers(req.headers || {});
|
|
171
|
+
if (to.origin !== from.origin) {
|
|
172
|
+
for (const k of [...headers.keys()]) if (!CROSS_ORIGIN_KEEP.has(k)) headers.delete(k);
|
|
173
|
+
}
|
|
174
|
+
const get = status === 303 || (status === 301 || status === 302) && (req.method || "GET").toUpperCase() !== "GET";
|
|
175
|
+
return get ? { ...req, method: "GET", body: void 0, headers } : { ...req, headers };
|
|
176
|
+
}
|
|
177
|
+
function guardedFetch(opt, base) {
|
|
178
|
+
const allow = (opt.allowHosts || []).map((h) => String(h).toLowerCase().replace(/^\[|\]$/g, ""));
|
|
179
|
+
const node = !opt.viaBase && inNode();
|
|
63
180
|
return (
|
|
64
181
|
/** @type {any} */
|
|
65
|
-
(async (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
for (let
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
await
|
|
182
|
+
(async (input, init = {}) => {
|
|
183
|
+
let url = checkUrl(String(input), allow);
|
|
184
|
+
let req = { ...init };
|
|
185
|
+
for (let hop = 0; ; hop++) {
|
|
186
|
+
const trusted = allow.includes(url.hostname.replace(/^\[|\]$/g, "").toLowerCase());
|
|
187
|
+
const res = node ? await nodeRequest(url, req, trusted) : await base(url.href, { ...req, redirect: "manual" });
|
|
188
|
+
if (res.type === "opaqueredirect") throw new Error(`${url.host} redirects; a redirect cannot be checked here (pass your own fetch to render)`);
|
|
189
|
+
const loc = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
|
|
190
|
+
if (!loc) return res;
|
|
191
|
+
await res.body?.cancel().catch(() => {
|
|
192
|
+
});
|
|
193
|
+
if (hop >= FETCH_DEFAULTS.hops) throw new Error(`Too many redirects from ${url.host}`);
|
|
194
|
+
const next = checkUrl(new URL(loc, url).href, allow);
|
|
195
|
+
req = redirectRequest(url, next, req, res.status);
|
|
196
|
+
url = next;
|
|
75
197
|
}
|
|
76
198
|
})
|
|
77
199
|
);
|
|
78
200
|
}
|
|
79
|
-
function
|
|
80
|
-
const
|
|
81
|
-
const
|
|
201
|
+
function boundedFetch(f4, opt) {
|
|
202
|
+
const ms0 = Number(opt.fetchTimeoutMs) > 0 ? Number(opt.fetchTimeoutMs) : FETCH_DEFAULTS.timeoutMs;
|
|
203
|
+
const max = Number(opt.maxFetchBytes) > 0 ? Number(opt.maxFetchBytes) : FETCH_DEFAULTS.maxBytes;
|
|
82
204
|
return (
|
|
83
205
|
/** @type {any} */
|
|
84
206
|
(async (input, init = {}) => {
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
207
|
+
const ms = Math.max(1, Math.min(ms0, opt.deadline ? opt.deadline - Date.now() : Infinity));
|
|
208
|
+
const ctl = new AbortController();
|
|
209
|
+
const timer = setTimeout(() => ctl.abort(Object.assign(new Error(`The data request took longer than ${Math.round(ms)} ms`), { status: 504 })), ms);
|
|
210
|
+
const signal = init.signal ? anySignal([init.signal, ctl.signal]) : ctl.signal;
|
|
211
|
+
let res;
|
|
212
|
+
const stop = new Promise((_, rej) => signal.addEventListener("abort", () => rej(signal.reason), { once: true }));
|
|
213
|
+
stop.catch(() => {
|
|
214
|
+
});
|
|
89
215
|
try {
|
|
90
|
-
|
|
91
|
-
} catch {
|
|
92
|
-
|
|
216
|
+
res = await Promise.race([f4(input, { ...init, signal }), stop]);
|
|
217
|
+
} catch (e) {
|
|
218
|
+
clearTimeout(timer);
|
|
219
|
+
throw ctl.signal.aborted ? ctl.signal.reason : fetchFailure(e, input);
|
|
93
220
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
221
|
+
if (!res.body || /** @type {any} */
|
|
222
|
+
init.pwStream) {
|
|
223
|
+
clearTimeout(timer);
|
|
224
|
+
return res;
|
|
97
225
|
}
|
|
98
|
-
|
|
99
|
-
|
|
226
|
+
let n = 0;
|
|
227
|
+
const reader = res.body.getReader();
|
|
228
|
+
const body = new ReadableStream({
|
|
229
|
+
async pull(c) {
|
|
230
|
+
try {
|
|
231
|
+
const { done, value } = await reader.read();
|
|
232
|
+
if (done) {
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
c.close();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
n += value.byteLength;
|
|
238
|
+
if (n > max) {
|
|
239
|
+
clearTimeout(timer);
|
|
240
|
+
reader.cancel().catch(() => {
|
|
241
|
+
});
|
|
242
|
+
c.error(Object.assign(new Error(`The data response is larger than ${Math.round(max / 1048576)} MB (maxFetchBytes)`), { status: 413 }));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
c.enqueue(value);
|
|
246
|
+
} catch (e) {
|
|
247
|
+
clearTimeout(timer);
|
|
248
|
+
c.error(ctl.signal.aborted ? ctl.signal.reason : e);
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
cancel(r) {
|
|
252
|
+
clearTimeout(timer);
|
|
253
|
+
return reader.cancel(r);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
|
|
100
257
|
})
|
|
101
258
|
);
|
|
102
259
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
260
|
+
var CAUSES = {
|
|
261
|
+
ENOTFOUND: "the host name did not resolve",
|
|
262
|
+
EAI_AGAIN: "DNS lookup timed out",
|
|
263
|
+
ECONNREFUSED: "connection refused",
|
|
264
|
+
ECONNRESET: "connection reset",
|
|
265
|
+
EPIPE: "connection closed",
|
|
266
|
+
UND_ERR_SOCKET: "connection closed early",
|
|
267
|
+
ETIMEDOUT: "timed out",
|
|
268
|
+
UND_ERR_CONNECT_TIMEOUT: "connect timed out",
|
|
269
|
+
EPROTO: "TLS error",
|
|
270
|
+
ERR_TLS_CERT_ALTNAME_INVALID: "TLS certificate does not match the host",
|
|
271
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: "TLS certificate is self-signed",
|
|
272
|
+
SELF_SIGNED_CERT_IN_CHAIN: "TLS certificate chain is untrusted",
|
|
273
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "TLS certificate chain is untrusted",
|
|
274
|
+
CERT_HAS_EXPIRED: "TLS certificate has expired"
|
|
275
|
+
};
|
|
276
|
+
function redactUrl(s) {
|
|
277
|
+
const str = String(s);
|
|
278
|
+
const q = str.search(/[?#]/);
|
|
279
|
+
const base = (q >= 0 ? str.slice(0, q) : str).replace(/^([a-z][\w+.-]*:\/\/)[^/?#]*@/i, "$1");
|
|
280
|
+
return q >= 0 ? `${base}${str[q]}…` : base;
|
|
281
|
+
}
|
|
282
|
+
function fetchFailure(e, input) {
|
|
283
|
+
if (!e || e.status) return e;
|
|
284
|
+
const code = String(e.cause?.code || e.code || "");
|
|
285
|
+
if (!code && e.message !== "fetch failed") return e;
|
|
286
|
+
const why = CAUSES[code] || (/^ERR_TLS|CERT_|SSL/.test(code) ? "TLS error" : "");
|
|
287
|
+
const at = redactUrl(input?.href ?? input);
|
|
288
|
+
return Object.assign(new Error(`the request to ${at} failed${why ? `: ${why}` : ""}${code ? ` (${code})` : ""}`), { code: e.code });
|
|
289
|
+
}
|
|
290
|
+
function anySignal(list) {
|
|
291
|
+
const S3 = (
|
|
292
|
+
/** @type {any} */
|
|
293
|
+
AbortSignal
|
|
294
|
+
);
|
|
295
|
+
if (S3.any) return S3.any(list);
|
|
296
|
+
const c = new AbortController();
|
|
297
|
+
for (const s of list) {
|
|
298
|
+
if (s.aborted) c.abort(s.reason);
|
|
299
|
+
else s.addEventListener("abort", () => c.abort(s.reason), { once: true });
|
|
300
|
+
}
|
|
301
|
+
return c.signal;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/viewer/dataFetch.js
|
|
305
|
+
function sqlCall(body, sqlPreview) {
|
|
306
|
+
let b;
|
|
307
|
+
try {
|
|
308
|
+
b = JSON.parse(String(body));
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
if (!b || typeof b !== "object" || Array.isArray(b)) return false;
|
|
313
|
+
const keys = Object.keys(b);
|
|
314
|
+
if (typeof b.report === "string" && typeof b.source === "string") return keys.every((k) => ["report", "source", "params"].includes(k));
|
|
315
|
+
return sqlPreview && typeof b.query === "string" && keys.every((k) => ["connection", "query", "params"].includes(k));
|
|
316
|
+
}
|
|
317
|
+
var policy = { allowHosts: [], unsafeFetch: false, fetch: null };
|
|
318
|
+
function setFetchPolicy({ allowHosts, unsafeFetch = false, fetch: f4 = null } = {}) {
|
|
319
|
+
policy = { allowHosts: (allowHosts || []).map((h) => String(h).toLowerCase().replace(/^\[|\]$/g, "")), unsafeFetch: !!unsafeFetch, fetch: typeof f4 === "function" ? f4 : null };
|
|
320
|
+
}
|
|
321
|
+
var POLICY_KEYS = ["allowHosts", "unsafeFetch", "allowPrivate"];
|
|
322
|
+
function policyKeysIn(def) {
|
|
323
|
+
const has = (o) => POLICY_KEYS.filter((k) => o && typeof o === "object" && Object.hasOwn(o, k));
|
|
324
|
+
return [...has(def), ...(Array.isArray(def?.dataSources) ? def.dataSources : []).flatMap((s) => has(s).map((k) => `${s.name}.${k}`))];
|
|
325
|
+
}
|
|
326
|
+
var fetchPolicy = () => policy;
|
|
327
|
+
function failed(e, url) {
|
|
328
|
+
if (e?.status || !(e instanceof TypeError)) return e;
|
|
329
|
+
return Object.assign(new Error(`the request to ${redactUrl(url)} failed (${e.message}): the server may not allow this page to read it (CORS: it must send Access-Control-Allow-Origin), the network or the server may be down, or the browser blocked it (a content security policy or an extension)`), { cause: e });
|
|
330
|
+
}
|
|
331
|
+
var DATA = [/^\/api\/sample\//, /^\/api\/data\/proxy$/];
|
|
332
|
+
var OURS = /^\/(?:$|api(?:\/|$)|admin(?:\/|$)|designer(?:\/|$)|viewer(?:\/|$)|login(?:\/|$)|embed(?:\/|$)|_next(?:\/|$))/i;
|
|
333
|
+
var BUSY = /* @__PURE__ */ new Set([429, 502, 503, 504]);
|
|
334
|
+
function withRetry(f4, { onRetry, retries = 3, wait = (ms) => new Promise((r) => setTimeout(r, ms)) } = {}) {
|
|
335
|
+
return (
|
|
336
|
+
/** @type {any} */
|
|
337
|
+
(async (url, init = {}) => {
|
|
338
|
+
const method = String(init.method || "GET").toUpperCase();
|
|
339
|
+
const read = method === "GET" || method === "HEAD" || method === "POST" && /\/api\/data\/sql$/.test(new URL(String(url), "http://x").pathname);
|
|
340
|
+
for (let attempt = 1; ; attempt++) {
|
|
341
|
+
const res = await f4(url, init);
|
|
342
|
+
if (!read || !BUSY.has(res.status) || attempt > retries) return res;
|
|
343
|
+
const after = Number(res.headers?.get?.("retry-after"));
|
|
344
|
+
const ms = Number.isFinite(after) && after >= 0 ? Math.min(1e4, after * 1e3) : Math.round(400 * 2 ** (attempt - 1) * (0.75 + Math.random() * 0.5));
|
|
345
|
+
onRetry?.({ attempt, max: retries, status: res.status });
|
|
346
|
+
await wait(ms);
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
function reportFetch(base, f0 = policy.fetch || ((...a) => fetch(...a)), { sqlPreview = false, onRetry, retries = 3, wait } = {}) {
|
|
352
|
+
const self = new URL(base).origin;
|
|
353
|
+
const { allowHosts, unsafeFetch } = policy;
|
|
354
|
+
const send = (url, init) => f0(url, init).catch((e) => {
|
|
355
|
+
throw failed(e, url);
|
|
356
|
+
});
|
|
357
|
+
const f4 = withRetry(send, { onRetry, retries, wait });
|
|
358
|
+
const guarded = unsafeFetch ? f4 : guardedFetch({ allowHosts, viaBase: true }, f4);
|
|
359
|
+
const other = (url, init) => guarded(url, init).catch((e) => {
|
|
360
|
+
if (e?.code === "EPRIVATE") e.message = e.message.replace(/pass allowHosts: (\[[^\]]*\]) to render/, "mountViewer({ allowHosts: $1 })");
|
|
361
|
+
else if (e && typeof e === "object") e.message = String(e.message).replace("(pass your own fetch to render)", "(a browser cannot see where it leads: use the URL it redirects to, or render on the server)");
|
|
362
|
+
throw e;
|
|
363
|
+
});
|
|
364
|
+
const out = (
|
|
365
|
+
/** @type {any} */
|
|
366
|
+
(async (input, init = {}) => {
|
|
367
|
+
const u = new URL(String(input instanceof Request ? input.url : input), base);
|
|
368
|
+
const method = String(init.method || "GET").toUpperCase();
|
|
369
|
+
if (u.origin !== self) return other(u.href, { ...init, credentials: "omit" });
|
|
370
|
+
let path;
|
|
371
|
+
try {
|
|
372
|
+
path = decodeURIComponent(u.pathname).replace(/\/{2,}/g, "/");
|
|
373
|
+
} catch {
|
|
374
|
+
path = null;
|
|
375
|
+
}
|
|
376
|
+
const read = path !== null && ["GET", "HEAD"].includes(method) && !/%2f|%5c/i.test(u.pathname) && (DATA.some((r) => r.test(path)) || !OURS.test(path));
|
|
377
|
+
if (!read && !(method === "POST" && u.pathname === "/api/data/sql" && sqlCall(init.body, sqlPreview))) {
|
|
378
|
+
throw Object.assign(new Error(`A data source may only read data from this ReportWright server (sample data, the data proxy, saved SQL), not ${method} ${path}.`), { status: 403 });
|
|
379
|
+
}
|
|
380
|
+
const ours = DATA.some((r) => r.test(u.pathname)) || u.pathname === "/api/data/sql";
|
|
381
|
+
return f4(u.href, { ...init, credentials: ours ? "same-origin" : "omit", redirect: "error" });
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
if (!unsafeFetch) out.hostRule = { allowHosts };
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/engine/expr/evaluate.js
|
|
389
|
+
var ROOTS = { fields: "Fields", parameters: "Parameters", globals: "Globals", parent: "Parent", theme: "Theme", partproperties: "PartProperties" };
|
|
390
|
+
var astCache = /* @__PURE__ */ new Map();
|
|
391
|
+
function compile(src) {
|
|
392
|
+
let a = astCache.get(src);
|
|
393
|
+
if (!a) {
|
|
394
|
+
a = parse(src);
|
|
395
|
+
if (astCache.size > 5e3) astCache.clear();
|
|
396
|
+
astCache.set(src, a);
|
|
397
|
+
}
|
|
398
|
+
return a;
|
|
399
|
+
}
|
|
400
|
+
var tplCache = /* @__PURE__ */ new Map();
|
|
401
|
+
function parseValue(v) {
|
|
402
|
+
if (typeof v !== "string") return { kind: "lit", v };
|
|
403
|
+
let p = tplCache.get(v);
|
|
404
|
+
if (p) {
|
|
405
|
+
if (p.kind === "err") throw p.error;
|
|
406
|
+
return p;
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
p = parseFresh(v);
|
|
410
|
+
} catch (e) {
|
|
411
|
+
if (!(e instanceof ExprError)) throw e;
|
|
412
|
+
p = { kind: "err", error: e };
|
|
413
|
+
}
|
|
414
|
+
if (tplCache.size > 5e3) tplCache.clear();
|
|
131
415
|
tplCache.set(v, p);
|
|
132
416
|
if (p.kind === "err") throw p.error;
|
|
133
417
|
return p;
|
|
@@ -1722,334 +2006,86 @@ function checkFunctions(list) {
|
|
|
1722
2006
|
errors.push(`${label}: ${e.message}`);
|
|
1723
2007
|
}
|
|
1724
2008
|
}
|
|
1725
|
-
return errors;
|
|
1726
|
-
}
|
|
1727
|
-
var METHODS = {
|
|
1728
|
-
padleft: "padleft",
|
|
1729
|
-
padright: "padright",
|
|
1730
|
-
substring: "substring",
|
|
1731
|
-
toupper: "ucase",
|
|
1732
|
-
toupperinvariant: "ucase",
|
|
1733
|
-
tolower: "lcase",
|
|
1734
|
-
tolowerinvariant: "lcase",
|
|
1735
|
-
trim: "trim",
|
|
1736
|
-
trimstart: "trimstart",
|
|
1737
|
-
trimend: "trimend",
|
|
1738
|
-
startswith: "startswith",
|
|
1739
|
-
endswith: "endswith",
|
|
1740
|
-
indexof: "indexof",
|
|
1741
|
-
lastindexof: "lastindexof",
|
|
1742
|
-
contains: "contains",
|
|
1743
|
-
replace: "replace",
|
|
1744
|
-
split: "split",
|
|
1745
|
-
tostring: "tostring"
|
|
1746
|
-
};
|
|
1747
|
-
function netFormat(f4, args, ctx) {
|
|
1748
|
-
let out = "";
|
|
1749
|
-
for (let i = 0; i < f4.length; i++) {
|
|
1750
|
-
const c = f4[i];
|
|
1751
|
-
if (c === "{" && f4[i + 1] === "{") {
|
|
1752
|
-
out += "{";
|
|
1753
|
-
i++;
|
|
1754
|
-
continue;
|
|
1755
|
-
}
|
|
1756
|
-
if (c === "}" && f4[i + 1] === "}") {
|
|
1757
|
-
out += "}";
|
|
1758
|
-
i++;
|
|
1759
|
-
continue;
|
|
1760
|
-
}
|
|
1761
|
-
if (c === "}") throw new ExprError('String.Format: a "}" without its "{"');
|
|
1762
|
-
if (c !== "{") {
|
|
1763
|
-
out += c;
|
|
1764
|
-
continue;
|
|
1765
|
-
}
|
|
1766
|
-
const e = f4.indexOf("}", i);
|
|
1767
|
-
if (e < 0) throw new ExprError('String.Format: a "{" without its "}"');
|
|
1768
|
-
const m = /^\s*(\d{1,3})\s*(?:,\s*(-?\d{1,4}))?\s*(?::([^}]*))?$/.exec(f4.slice(i + 1, e));
|
|
1769
|
-
if (!m) throw new ExprError(`String.Format: "{${f4.slice(i + 1, e).slice(0, 20)}}" is not a placeholder`);
|
|
1770
|
-
const k = Number(m[1]);
|
|
1771
|
-
if (k >= args.length) throw new ExprError(`String.Format: {${k}} has no argument`);
|
|
1772
|
-
let t = toText(args[k], m[3] || null, ctx);
|
|
1773
|
-
const w = m[2] ? Number(m[2]) : 0;
|
|
1774
|
-
if (Math.abs(w) > t.length) t = w < 0 ? t.padEnd(-w) : t.padStart(w);
|
|
1775
|
-
capText("String.Format", out.length + t.length + f4.length - e);
|
|
1776
|
-
out += t;
|
|
1777
|
-
i = e;
|
|
1778
|
-
}
|
|
1779
|
-
return out;
|
|
1780
|
-
}
|
|
1781
|
-
function callCustom(n, name, ctx) {
|
|
1782
|
-
if (n.obj && n.obj.type === "id" && n.obj.name.toLowerCase() === "string" && name === "format") {
|
|
1783
|
-
if (!n.args.length) throw new ExprError("String.Format needs a format", n.p);
|
|
1784
|
-
const [f5, ...rest] = n.args.map((a) => evaluate(a, ctx));
|
|
1785
|
-
return netFormat(S(f5), rest, ctx);
|
|
1786
|
-
}
|
|
1787
|
-
const viaValue = n.obj && !(n.obj.type === "id" && n.obj.name.toLowerCase() === "code");
|
|
1788
|
-
if (viaValue && Object.hasOwn(METHODS, name)) {
|
|
1789
|
-
return FUNCTIONS[METHODS[name]](ctx, evaluate(n.obj, ctx), ...n.args.map((a) => evaluate(a, ctx)));
|
|
1790
|
-
}
|
|
1791
|
-
if (viaValue && n.name in Object.prototype) throw new ExprError(`The value has no member ${n.name}`, n.p);
|
|
1792
|
-
const viaCode = !!n.obj;
|
|
1793
|
-
if (viaCode && !(n.obj.type === "id" && n.obj.name.toLowerCase() === "code")) throw new ExprError(`"${n.name}(…)" after "." is not supported; report functions are called as Code.${n.name}(…)`, n.p);
|
|
1794
|
-
const table = ctx.functions;
|
|
1795
|
-
const f4 = table?.byName.get(name);
|
|
1796
|
-
if (!f4 || !viaCode && builtin(name)) throw new ExprError(viaCode ? `The report has no function "${n.name}"` : `Unknown function "${n.name}"`, n.p);
|
|
1797
|
-
const params = Array.isArray(f4.params) ? f4.params : [];
|
|
1798
|
-
if (n.args.length !== params.length) throw new ExprError(`${f4.name} needs ${params.length} argument${params.length === 1 ? "" : "s"}`, n.p);
|
|
1799
|
-
const depth = (ctx.fnDepth || 0) + 1;
|
|
1800
|
-
if (depth > MAX_FN_DEPTH) throw new ExprError(`${f4.name}: functions call each other more than ${MAX_FN_DEPTH} deep`, n.p);
|
|
1801
|
-
if (++table.count.n > MAX_FN_CALLS) throw new ExprError(`Report functions were called more than 1,000,000 times in this render`, n.p);
|
|
1802
|
-
const locals = /* @__PURE__ */ Object.create(null);
|
|
1803
|
-
for (let i = 0; i < params.length; i++) locals[String(params[i]).toLowerCase()] = evaluate(n.args[i], ctx);
|
|
1804
|
-
return evaluate(compile(String(f4.body).replace(/^=/, "")), Object.assign({}, ctx, { locals, fnDepth: depth }));
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
// src/engine/data/guard.js
|
|
1808
|
-
var FETCH_DEFAULTS = { timeoutMs: 3e4, maxBytes: 64 * 1048576, hops: 5 };
|
|
1809
|
-
var V4 = [
|
|
1810
|
-
["0.0.0.0", 8],
|
|
1811
|
-
["10.0.0.0", 8],
|
|
1812
|
-
["100.64.0.0", 10],
|
|
1813
|
-
["127.0.0.0", 8],
|
|
1814
|
-
["169.254.0.0", 16],
|
|
1815
|
-
["172.16.0.0", 12],
|
|
1816
|
-
["192.0.0.0", 24],
|
|
1817
|
-
["192.0.2.0", 24],
|
|
1818
|
-
["192.168.0.0", 16],
|
|
1819
|
-
["198.18.0.0", 15],
|
|
1820
|
-
["198.51.100.0", 24],
|
|
1821
|
-
["203.0.113.0", 24],
|
|
1822
|
-
["224.0.0.0", 3]
|
|
1823
|
-
].map(([a, p]) => [v4num(String(a)), Number(p)]);
|
|
1824
|
-
function v4num(ip) {
|
|
1825
|
-
return ip.split(".").reduce((n, x) => n * 256 + Number(x), 0);
|
|
1826
|
-
}
|
|
1827
|
-
function privateV4(ip) {
|
|
1828
|
-
const n = v4num(ip);
|
|
1829
|
-
return V4.some(([a, p]) => Math.floor(n / 2 ** (32 - p)) === Math.floor(a / 2 ** (32 - p)));
|
|
1830
|
-
}
|
|
1831
|
-
function groups6(ip) {
|
|
1832
|
-
const [h, t = ""] = ip.split("::");
|
|
1833
|
-
const a = h ? h.split(":") : [], b = t ? t.split(":") : [];
|
|
1834
|
-
if (b.length && b[b.length - 1].includes(".")) {
|
|
1835
|
-
const q = b.pop().split(".").map(Number);
|
|
1836
|
-
b.push((q[0] << 8 | q[1]).toString(16), (q[2] << 8 | q[3]).toString(16));
|
|
1837
|
-
}
|
|
1838
|
-
return [...a, ...Array(Math.max(0, 8 - a.length - b.length)).fill("0"), ...b].map((x) => parseInt(x, 16) || 0);
|
|
1839
|
-
}
|
|
1840
|
-
function privateV6(ip) {
|
|
1841
|
-
const g = groups6(ip);
|
|
1842
|
-
const v4 = () => `${g[6] >> 8}.${g[6] & 255}.${g[7] >> 8}.${g[7] & 255}`;
|
|
1843
|
-
if (g.slice(0, 6).every((x) => x === 0)) return true;
|
|
1844
|
-
if (g.slice(0, 5).every((x) => x === 0) && g[5] === 65535) return privateV4(v4());
|
|
1845
|
-
if (g[0] === 100 && g[1] === 65435) return g[2] === 1 || privateV4(v4());
|
|
1846
|
-
if (g[0] === 8194 || g[0] === 8193 && g[1] === 0) return true;
|
|
1847
|
-
if ((g[0] & 65024) === 64512 || (g[0] & 65472) === 65152 || (g[0] & 65472) === 65216 || (g[0] & 65280) === 65280) return true;
|
|
1848
|
-
return false;
|
|
1849
|
-
}
|
|
1850
|
-
function privateHost(host) {
|
|
1851
|
-
const h = host.replace(/^\[|\]$/g, "").toLowerCase();
|
|
1852
|
-
if (h.includes(":")) return privateV6(h);
|
|
1853
|
-
if (/^\d+\.\d+\.\d+\.\d+$/.test(h)) return privateV4(h);
|
|
1854
|
-
return h === "localhost" || h.endsWith(".localhost") || h.endsWith(".local") || h.endsWith(".internal");
|
|
1855
|
-
}
|
|
1856
|
-
var refuse = (host) => Object.assign(new Error(`"${host}" is a private or local address; a data source may not reach it (pass allowHosts: ["${host}"] to render to allow it)`), { status: 403, code: "EPRIVATE" });
|
|
1857
|
-
function nodeMod(name) {
|
|
1858
|
-
const p = (
|
|
1859
|
-
/** @type {any} */
|
|
1860
|
-
globalThis.process
|
|
1861
|
-
);
|
|
1862
|
-
if (!p?.versions?.node) return null;
|
|
1863
|
-
if (!p.getBuiltinModule) throw Object.assign(new Error("The engine's fetch guard needs Node 20.16 or later (process.getBuiltinModule); or pass your own fetch to render"), { status: 500 });
|
|
1864
|
-
return p.getBuiltinModule(name);
|
|
1865
|
-
}
|
|
1866
|
-
var inNode = () => !!/** @type {any} */
|
|
1867
|
-
globalThis.process?.versions?.node;
|
|
1868
|
-
var addrHost = (a) => a.family === 6 || String(a.address).includes(":") ? `[${a.address}]` : String(a.address);
|
|
1869
|
-
function checkUrl(input, allow) {
|
|
1870
|
-
let u;
|
|
1871
|
-
try {
|
|
1872
|
-
u = new URL(input);
|
|
1873
|
-
} catch {
|
|
1874
|
-
throw Object.assign(new Error(`Not a URL: ${String(input).slice(0, 200)}`), { status: 400 });
|
|
1875
|
-
}
|
|
1876
|
-
if (u.protocol !== "http:" && u.protocol !== "https:") throw Object.assign(new Error(`Only http and https URLs can be fetched, not ${u.protocol}`), { status: 400 });
|
|
1877
|
-
if (u.username || u.password) throw Object.assign(new Error("A user name or password in a data URL is not allowed"), { status: 400 });
|
|
1878
|
-
const host = u.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
1879
|
-
if (!host) throw Object.assign(new Error("A data URL needs a host"), { status: 400 });
|
|
1880
|
-
if (allow.includes(host)) return u;
|
|
1881
|
-
let bad = true;
|
|
1882
|
-
try {
|
|
1883
|
-
bad = privateHost(u.hostname);
|
|
1884
|
-
} catch {
|
|
1885
|
-
}
|
|
1886
|
-
if (bad) throw refuse(host);
|
|
1887
|
-
return u;
|
|
1888
|
-
}
|
|
1889
|
-
function nodeRequest(url, init, trusted) {
|
|
1890
|
-
const http = nodeMod("node:http"), https = nodeMod("node:https"), dns = nodeMod("node:dns"), stream = nodeMod("node:stream");
|
|
1891
|
-
const lookup = (name, o, cb) => dns.lookup(name, { ...o, all: true }, (err, list) => {
|
|
1892
|
-
if (err || !Array.isArray(list) || !list.length) return cb(err || refuse(name));
|
|
1893
|
-
let bad = true;
|
|
1894
|
-
try {
|
|
1895
|
-
bad = !trusted && list.some((a) => privateHost(addrHost(a)));
|
|
1896
|
-
} catch {
|
|
1897
|
-
}
|
|
1898
|
-
if (bad) return cb(refuse(name));
|
|
1899
|
-
return o.all ? cb(null, list) : cb(null, list[0].address, list[0].family);
|
|
1900
|
-
});
|
|
1901
|
-
const headers = Object.fromEntries(new Headers(init.headers || {}));
|
|
1902
|
-
headers["accept-encoding"] = "identity";
|
|
1903
|
-
return new Promise((resolve, reject) => {
|
|
1904
|
-
const req = (url.protocol === "https:" ? https : http).request(url, { method: init.method || "GET", headers, lookup, signal: init.signal }, (res) => {
|
|
1905
|
-
const pairs = [];
|
|
1906
|
-
for (let i = 0; i < res.rawHeaders.length; i += 2) pairs.push([res.rawHeaders[i], res.rawHeaders[i + 1]]);
|
|
1907
|
-
const none = [101, 204, 205, 304].includes(res.statusCode) || (init.method || "GET").toUpperCase() === "HEAD";
|
|
1908
|
-
if (none) res.resume();
|
|
1909
|
-
resolve(new Response(none ? null : stream.Readable.toWeb(res), { status: res.statusCode, statusText: res.statusMessage, headers: (
|
|
1910
|
-
/** @type {any} */
|
|
1911
|
-
pairs
|
|
1912
|
-
) }));
|
|
1913
|
-
});
|
|
1914
|
-
req.on("error", reject);
|
|
1915
|
-
req.end(init.body ?? void 0);
|
|
1916
|
-
});
|
|
1917
|
-
}
|
|
1918
|
-
var CROSS_ORIGIN_KEEP = /* @__PURE__ */ new Set(["accept", "accept-language", "content-type"]);
|
|
1919
|
-
function redirectRequest(from, to, req, status) {
|
|
1920
|
-
if (from.protocol === "https:" && to.protocol !== "https:") throw Object.assign(new Error(`${from.host} redirects from https to http; not followed`), { status: 403 });
|
|
1921
|
-
const headers = new Headers(req.headers || {});
|
|
1922
|
-
if (to.origin !== from.origin) {
|
|
1923
|
-
for (const k of [...headers.keys()]) if (!CROSS_ORIGIN_KEEP.has(k)) headers.delete(k);
|
|
1924
|
-
}
|
|
1925
|
-
const get = status === 303 || (status === 301 || status === 302) && (req.method || "GET").toUpperCase() !== "GET";
|
|
1926
|
-
return get ? { ...req, method: "GET", body: void 0, headers } : { ...req, headers };
|
|
1927
|
-
}
|
|
1928
|
-
function guardedFetch(opt, base) {
|
|
1929
|
-
const allow = (opt.allowHosts || []).map((h) => String(h).toLowerCase().replace(/^\[|\]$/g, ""));
|
|
1930
|
-
const node = inNode();
|
|
1931
|
-
return (
|
|
1932
|
-
/** @type {any} */
|
|
1933
|
-
(async (input, init = {}) => {
|
|
1934
|
-
let url = checkUrl(String(input), allow);
|
|
1935
|
-
let req = { ...init };
|
|
1936
|
-
for (let hop = 0; ; hop++) {
|
|
1937
|
-
const trusted = allow.includes(url.hostname.replace(/^\[|\]$/g, "").toLowerCase());
|
|
1938
|
-
const res = node ? await nodeRequest(url, req, trusted) : await base(url.href, { ...req, redirect: "manual" });
|
|
1939
|
-
if (res.type === "opaqueredirect") throw new Error(`${url.host} redirects; a redirect cannot be checked here (pass your own fetch to render)`);
|
|
1940
|
-
const loc = res.status >= 300 && res.status < 400 ? res.headers.get("location") : null;
|
|
1941
|
-
if (!loc) return res;
|
|
1942
|
-
await res.body?.cancel().catch(() => {
|
|
1943
|
-
});
|
|
1944
|
-
if (hop >= FETCH_DEFAULTS.hops) throw new Error(`Too many redirects from ${url.host}`);
|
|
1945
|
-
const next = checkUrl(new URL(loc, url).href, allow);
|
|
1946
|
-
req = redirectRequest(url, next, req, res.status);
|
|
1947
|
-
url = next;
|
|
1948
|
-
}
|
|
1949
|
-
})
|
|
1950
|
-
);
|
|
1951
|
-
}
|
|
1952
|
-
function boundedFetch(f4, opt) {
|
|
1953
|
-
const ms0 = Number(opt.fetchTimeoutMs) > 0 ? Number(opt.fetchTimeoutMs) : FETCH_DEFAULTS.timeoutMs;
|
|
1954
|
-
const max = Number(opt.maxFetchBytes) > 0 ? Number(opt.maxFetchBytes) : FETCH_DEFAULTS.maxBytes;
|
|
1955
|
-
return (
|
|
1956
|
-
/** @type {any} */
|
|
1957
|
-
(async (input, init = {}) => {
|
|
1958
|
-
const ms = Math.max(1, Math.min(ms0, opt.deadline ? opt.deadline - Date.now() : Infinity));
|
|
1959
|
-
const ctl = new AbortController();
|
|
1960
|
-
const timer = setTimeout(() => ctl.abort(Object.assign(new Error(`The data request took longer than ${Math.round(ms)} ms`), { status: 504 })), ms);
|
|
1961
|
-
const signal = init.signal ? anySignal([init.signal, ctl.signal]) : ctl.signal;
|
|
1962
|
-
let res;
|
|
1963
|
-
const stop = new Promise((_, rej) => signal.addEventListener("abort", () => rej(signal.reason), { once: true }));
|
|
1964
|
-
stop.catch(() => {
|
|
1965
|
-
});
|
|
1966
|
-
try {
|
|
1967
|
-
res = await Promise.race([f4(input, { ...init, signal }), stop]);
|
|
1968
|
-
} catch (e) {
|
|
1969
|
-
clearTimeout(timer);
|
|
1970
|
-
throw ctl.signal.aborted ? ctl.signal.reason : fetchFailure(e, input);
|
|
1971
|
-
}
|
|
1972
|
-
if (!res.body || /** @type {any} */
|
|
1973
|
-
init.pwStream) {
|
|
1974
|
-
clearTimeout(timer);
|
|
1975
|
-
return res;
|
|
1976
|
-
}
|
|
1977
|
-
let n = 0;
|
|
1978
|
-
const reader = res.body.getReader();
|
|
1979
|
-
const body = new ReadableStream({
|
|
1980
|
-
async pull(c) {
|
|
1981
|
-
try {
|
|
1982
|
-
const { done, value } = await reader.read();
|
|
1983
|
-
if (done) {
|
|
1984
|
-
clearTimeout(timer);
|
|
1985
|
-
c.close();
|
|
1986
|
-
return;
|
|
1987
|
-
}
|
|
1988
|
-
n += value.byteLength;
|
|
1989
|
-
if (n > max) {
|
|
1990
|
-
clearTimeout(timer);
|
|
1991
|
-
reader.cancel().catch(() => {
|
|
1992
|
-
});
|
|
1993
|
-
c.error(Object.assign(new Error(`The data response is larger than ${Math.round(max / 1048576)} MB (maxFetchBytes)`), { status: 413 }));
|
|
1994
|
-
return;
|
|
1995
|
-
}
|
|
1996
|
-
c.enqueue(value);
|
|
1997
|
-
} catch (e) {
|
|
1998
|
-
clearTimeout(timer);
|
|
1999
|
-
c.error(ctl.signal.aborted ? ctl.signal.reason : e);
|
|
2000
|
-
}
|
|
2001
|
-
},
|
|
2002
|
-
cancel(r) {
|
|
2003
|
-
clearTimeout(timer);
|
|
2004
|
-
return reader.cancel(r);
|
|
2005
|
-
}
|
|
2006
|
-
});
|
|
2007
|
-
return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
|
|
2008
|
-
})
|
|
2009
|
-
);
|
|
2010
|
-
}
|
|
2011
|
-
var CAUSES = {
|
|
2012
|
-
ENOTFOUND: "the host name did not resolve",
|
|
2013
|
-
EAI_AGAIN: "DNS lookup timed out",
|
|
2014
|
-
ECONNREFUSED: "connection refused",
|
|
2015
|
-
ECONNRESET: "connection reset",
|
|
2016
|
-
EPIPE: "connection closed",
|
|
2017
|
-
UND_ERR_SOCKET: "connection closed early",
|
|
2018
|
-
ETIMEDOUT: "timed out",
|
|
2019
|
-
UND_ERR_CONNECT_TIMEOUT: "connect timed out",
|
|
2020
|
-
EPROTO: "TLS error",
|
|
2021
|
-
ERR_TLS_CERT_ALTNAME_INVALID: "TLS certificate does not match the host",
|
|
2022
|
-
DEPTH_ZERO_SELF_SIGNED_CERT: "TLS certificate is self-signed",
|
|
2023
|
-
SELF_SIGNED_CERT_IN_CHAIN: "TLS certificate chain is untrusted",
|
|
2024
|
-
UNABLE_TO_VERIFY_LEAF_SIGNATURE: "TLS certificate chain is untrusted",
|
|
2025
|
-
CERT_HAS_EXPIRED: "TLS certificate has expired"
|
|
2026
|
-
};
|
|
2027
|
-
function redactUrl(s) {
|
|
2028
|
-
const str = String(s);
|
|
2029
|
-
const q = str.search(/[?#]/);
|
|
2030
|
-
const base = (q >= 0 ? str.slice(0, q) : str).replace(/^([a-z][\w+.-]*:\/\/)[^/?#]*@/i, "$1");
|
|
2031
|
-
return q >= 0 ? `${base}${str[q]}…` : base;
|
|
2009
|
+
return errors;
|
|
2032
2010
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2011
|
+
var METHODS = {
|
|
2012
|
+
padleft: "padleft",
|
|
2013
|
+
padright: "padright",
|
|
2014
|
+
substring: "substring",
|
|
2015
|
+
toupper: "ucase",
|
|
2016
|
+
toupperinvariant: "ucase",
|
|
2017
|
+
tolower: "lcase",
|
|
2018
|
+
tolowerinvariant: "lcase",
|
|
2019
|
+
trim: "trim",
|
|
2020
|
+
trimstart: "trimstart",
|
|
2021
|
+
trimend: "trimend",
|
|
2022
|
+
startswith: "startswith",
|
|
2023
|
+
endswith: "endswith",
|
|
2024
|
+
indexof: "indexof",
|
|
2025
|
+
lastindexof: "lastindexof",
|
|
2026
|
+
contains: "contains",
|
|
2027
|
+
replace: "replace",
|
|
2028
|
+
split: "split",
|
|
2029
|
+
tostring: "tostring"
|
|
2030
|
+
};
|
|
2031
|
+
function netFormat(f4, args, ctx) {
|
|
2032
|
+
let out = "";
|
|
2033
|
+
for (let i = 0; i < f4.length; i++) {
|
|
2034
|
+
const c = f4[i];
|
|
2035
|
+
if (c === "{" && f4[i + 1] === "{") {
|
|
2036
|
+
out += "{";
|
|
2037
|
+
i++;
|
|
2038
|
+
continue;
|
|
2039
|
+
}
|
|
2040
|
+
if (c === "}" && f4[i + 1] === "}") {
|
|
2041
|
+
out += "}";
|
|
2042
|
+
i++;
|
|
2043
|
+
continue;
|
|
2044
|
+
}
|
|
2045
|
+
if (c === "}") throw new ExprError('String.Format: a "}" without its "{"');
|
|
2046
|
+
if (c !== "{") {
|
|
2047
|
+
out += c;
|
|
2048
|
+
continue;
|
|
2049
|
+
}
|
|
2050
|
+
const e = f4.indexOf("}", i);
|
|
2051
|
+
if (e < 0) throw new ExprError('String.Format: a "{" without its "}"');
|
|
2052
|
+
const m = /^\s*(\d{1,3})\s*(?:,\s*(-?\d{1,4}))?\s*(?::([^}]*))?$/.exec(f4.slice(i + 1, e));
|
|
2053
|
+
if (!m) throw new ExprError(`String.Format: "{${f4.slice(i + 1, e).slice(0, 20)}}" is not a placeholder`);
|
|
2054
|
+
const k = Number(m[1]);
|
|
2055
|
+
if (k >= args.length) throw new ExprError(`String.Format: {${k}} has no argument`);
|
|
2056
|
+
let t = toText(args[k], m[3] || null, ctx);
|
|
2057
|
+
const w = m[2] ? Number(m[2]) : 0;
|
|
2058
|
+
if (Math.abs(w) > t.length) t = w < 0 ? t.padEnd(-w) : t.padStart(w);
|
|
2059
|
+
capText("String.Format", out.length + t.length + f4.length - e);
|
|
2060
|
+
out += t;
|
|
2061
|
+
i = e;
|
|
2062
|
+
}
|
|
2063
|
+
return out;
|
|
2040
2064
|
}
|
|
2041
|
-
function
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
if (S3.any) return S3.any(list);
|
|
2047
|
-
const c = new AbortController();
|
|
2048
|
-
for (const s of list) {
|
|
2049
|
-
if (s.aborted) c.abort(s.reason);
|
|
2050
|
-
else s.addEventListener("abort", () => c.abort(s.reason), { once: true });
|
|
2065
|
+
function callCustom(n, name, ctx) {
|
|
2066
|
+
if (n.obj && n.obj.type === "id" && n.obj.name.toLowerCase() === "string" && name === "format") {
|
|
2067
|
+
if (!n.args.length) throw new ExprError("String.Format needs a format", n.p);
|
|
2068
|
+
const [f5, ...rest] = n.args.map((a) => evaluate(a, ctx));
|
|
2069
|
+
return netFormat(S(f5), rest, ctx);
|
|
2051
2070
|
}
|
|
2052
|
-
|
|
2071
|
+
const viaValue = n.obj && !(n.obj.type === "id" && n.obj.name.toLowerCase() === "code");
|
|
2072
|
+
if (viaValue && Object.hasOwn(METHODS, name)) {
|
|
2073
|
+
return FUNCTIONS[METHODS[name]](ctx, evaluate(n.obj, ctx), ...n.args.map((a) => evaluate(a, ctx)));
|
|
2074
|
+
}
|
|
2075
|
+
if (viaValue && n.name in Object.prototype) throw new ExprError(`The value has no member ${n.name}`, n.p);
|
|
2076
|
+
const viaCode = !!n.obj;
|
|
2077
|
+
if (viaCode && !(n.obj.type === "id" && n.obj.name.toLowerCase() === "code")) throw new ExprError(`"${n.name}(…)" after "." is not supported; report functions are called as Code.${n.name}(…)`, n.p);
|
|
2078
|
+
const table = ctx.functions;
|
|
2079
|
+
const f4 = table?.byName.get(name);
|
|
2080
|
+
if (!f4 || !viaCode && builtin(name)) throw new ExprError(viaCode ? `The report has no function "${n.name}"` : `Unknown function "${n.name}"`, n.p);
|
|
2081
|
+
const params = Array.isArray(f4.params) ? f4.params : [];
|
|
2082
|
+
if (n.args.length !== params.length) throw new ExprError(`${f4.name} needs ${params.length} argument${params.length === 1 ? "" : "s"}`, n.p);
|
|
2083
|
+
const depth = (ctx.fnDepth || 0) + 1;
|
|
2084
|
+
if (depth > MAX_FN_DEPTH) throw new ExprError(`${f4.name}: functions call each other more than ${MAX_FN_DEPTH} deep`, n.p);
|
|
2085
|
+
if (++table.count.n > MAX_FN_CALLS) throw new ExprError(`Report functions were called more than 1,000,000 times in this render`, n.p);
|
|
2086
|
+
const locals = /* @__PURE__ */ Object.create(null);
|
|
2087
|
+
for (let i = 0; i < params.length; i++) locals[String(params[i]).toLowerCase()] = evaluate(n.args[i], ctx);
|
|
2088
|
+
return evaluate(compile(String(f4.body).replace(/^=/, "")), Object.assign({}, ctx, { locals, fnDepth: depth }));
|
|
2053
2089
|
}
|
|
2054
2090
|
|
|
2055
2091
|
// src/importers/xml.js
|
|
@@ -2850,7 +2886,10 @@ function fetcherFor(opt) {
|
|
|
2850
2886
|
return boundedFetch(f4, opt);
|
|
2851
2887
|
}
|
|
2852
2888
|
function checkHostSource(src, url, opt) {
|
|
2853
|
-
|
|
2889
|
+
const rule = opt.fetch ? opt.fetch.hostRule : opt.unsafeFetch ? null : opt;
|
|
2890
|
+
if (!rule) return;
|
|
2891
|
+
const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(String(url).trim());
|
|
2892
|
+
if (scheme && !/^https?$/i.test(scheme[1])) throw Object.assign(new Error(`Only http and https URLs can be fetched, not ${scheme[1].toLowerCase()}:`), { status: 400 });
|
|
2854
2893
|
const t = String(src.url ?? "").trim();
|
|
2855
2894
|
if (/^\/(?![/\\])/.test(t)) return;
|
|
2856
2895
|
const m = /^https?:\/\/([^/?#\\]*)/i.exec(t);
|
|
@@ -2861,7 +2900,7 @@ function checkHostSource(src, url, opt) {
|
|
|
2861
2900
|
host = new URL(url).hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
2862
2901
|
} catch {
|
|
2863
2902
|
}
|
|
2864
|
-
if (whole && host && (
|
|
2903
|
+
if (whole && host && (rule.allowHosts || []).map((h) => String(h).toLowerCase()).includes(host)) return;
|
|
2865
2904
|
throw Object.assign(new Error("its host is made by a parameter or an expression: write the scheme and host out (parameters may fill the path and query), or make the whole URL a parameter and list its host in allowHosts"), { status: 403 });
|
|
2866
2905
|
}
|
|
2867
2906
|
async function loadSources(def, opt) {
|
|
@@ -3142,18 +3181,18 @@ function buildDataSets(def, sources, baseCtx) {
|
|
|
3142
3181
|
});
|
|
3143
3182
|
if (calc.length) {
|
|
3144
3183
|
const c = { ...baseCtx, dataSets: { ...baseCtx.dataSets || {}, ...sets }, fields: null };
|
|
3145
|
-
const
|
|
3184
|
+
const failed2 = /* @__PURE__ */ new Set();
|
|
3146
3185
|
for (const r of rows) {
|
|
3147
3186
|
c.fields = r;
|
|
3148
3187
|
for (const fd of calc) {
|
|
3149
|
-
if (
|
|
3188
|
+
if (failed2.has(fd.name)) {
|
|
3150
3189
|
r[fd.name] = null;
|
|
3151
3190
|
continue;
|
|
3152
3191
|
}
|
|
3153
3192
|
try {
|
|
3154
3193
|
r[fd.name] = coerce(evalValue(fd.expr, c), fd.type, opts.get(fd));
|
|
3155
3194
|
} catch (e) {
|
|
3156
|
-
|
|
3195
|
+
failed2.add(fd.name);
|
|
3157
3196
|
r[fd.name] = null;
|
|
3158
3197
|
baseCtx.warn?.(`Data set "${ds.name}", field "${fd.name}": ${e.message}`);
|
|
3159
3198
|
}
|
|
@@ -3701,543 +3740,6 @@ function normBox(v) {
|
|
|
3701
3740
|
return [a[0], a[1], a[2], a[3]];
|
|
3702
3741
|
}
|
|
3703
3742
|
|
|
3704
|
-
// src/engine/text/measure.js
|
|
3705
|
-
import bidiFactory from "bidi-js";
|
|
3706
|
-
var bidi = bidiFactory();
|
|
3707
|
-
function special(cp) {
|
|
3708
|
-
if (cp < 1424) return false;
|
|
3709
|
-
if (cp <= 7423) return true;
|
|
3710
|
-
if (cp >= 8192 && cp <= 8303) return cp >= 8203 && cp <= 8207 || cp >= 8234 && cp <= 8238 || cp >= 8288;
|
|
3711
|
-
return cp >= 11904 && cp <= 42191 || cp >= 43056 && cp <= 44031 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 64285 && cp <= 65023 || cp >= 65024 && cp <= 65039 || cp >= 65072 && cp <= 65103 || cp >= 65136 && cp <= 65278 || cp >= 65280 && cp <= 65519 || cp >= 65536;
|
|
3712
|
-
}
|
|
3713
|
-
var cjk = (cp) => cp >= 11904 && cp <= 40959 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65280 && cp <= 65519 || cp >= 131072 && cp <= 262143;
|
|
3714
|
-
var NO_START = new Set("、。,.・:;?!ー)」』】〕〉》〗〙〟’”}⦆ぁぃぅぇぉっゃゅょゎゕゖァィゥェォッャュョヮヵヶ々〻‐゠–〜~…‥,.!?:;)]}%ゝゞヽヾ゛゜ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ。」、・ー%]⦆");
|
|
3715
|
-
var NO_END = new Set("(「『【〔〈《〖〘〝‘“{⦅([{[「$¥£");
|
|
3716
|
-
var MAYBE = /[^\u0000-\u058f]/;
|
|
3717
|
-
var RTL_OR_CONTROLS = /[-ࣿיִ-﷿ﹰ---]|[\uD802\uD803\uD83A]/;
|
|
3718
|
-
var SCRIPTS = [
|
|
3719
|
-
"Latin",
|
|
3720
|
-
"Greek",
|
|
3721
|
-
"Cyrillic",
|
|
3722
|
-
"Armenian",
|
|
3723
|
-
"Hebrew",
|
|
3724
|
-
"Arabic",
|
|
3725
|
-
"Syriac",
|
|
3726
|
-
"Thaana",
|
|
3727
|
-
"Devanagari",
|
|
3728
|
-
"Bengali",
|
|
3729
|
-
"Gurmukhi",
|
|
3730
|
-
"Gujarati",
|
|
3731
|
-
"Oriya",
|
|
3732
|
-
"Tamil",
|
|
3733
|
-
"Telugu",
|
|
3734
|
-
"Kannada",
|
|
3735
|
-
"Malayalam",
|
|
3736
|
-
"Sinhala",
|
|
3737
|
-
"Thai",
|
|
3738
|
-
"Lao",
|
|
3739
|
-
"Tibetan",
|
|
3740
|
-
"Myanmar",
|
|
3741
|
-
"Georgian",
|
|
3742
|
-
"Hangul",
|
|
3743
|
-
"Ethiopic",
|
|
3744
|
-
"Khmer",
|
|
3745
|
-
"Mongolian",
|
|
3746
|
-
"Han",
|
|
3747
|
-
"Hiragana",
|
|
3748
|
-
"Katakana",
|
|
3749
|
-
"Bopomofo"
|
|
3750
|
-
].map((s) => (
|
|
3751
|
-
/** @type {[string, RegExp]} */
|
|
3752
|
-
[s, new RegExp(`\\p{Script=${s}}`, "u")]
|
|
3753
|
-
));
|
|
3754
|
-
var SIMPLE = /* @__PURE__ */ new Set(["Latin", "Greek", "Cyrillic", "Armenian", "Georgian", "Hangul", "Han", "Hiragana", "Katakana", "Bopomofo", "Common"]);
|
|
3755
|
-
var STICKY = /[\p{M}\p{Cf}]/u;
|
|
3756
|
-
var SPACE = /\s/u;
|
|
3757
|
-
var scriptCache = /* @__PURE__ */ new Map();
|
|
3758
|
-
function scriptOf(cp) {
|
|
3759
|
-
let s = scriptCache.get(cp);
|
|
3760
|
-
if (s === void 0) {
|
|
3761
|
-
const ch = String.fromCodePoint(cp);
|
|
3762
|
-
s = "Common";
|
|
3763
|
-
for (const [name, re] of SCRIPTS) if (re.test(ch)) {
|
|
3764
|
-
s = name;
|
|
3765
|
-
break;
|
|
3766
|
-
}
|
|
3767
|
-
if (scriptCache.size >= 2e4) scriptCache.clear();
|
|
3768
|
-
scriptCache.set(cp, s);
|
|
3769
|
-
}
|
|
3770
|
-
return s;
|
|
3771
|
-
}
|
|
3772
|
-
var INTL = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function";
|
|
3773
|
-
var GRAPHEMES = INTL ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
|
|
3774
|
-
var JOINS = /[\p{M}\u200c\u200d\ufe00-\ufe0f]/u;
|
|
3775
|
-
function clusters(s, intl = true) {
|
|
3776
|
-
if (intl && GRAPHEMES) return [...GRAPHEMES.segment(s)].map((x) => x.segment);
|
|
3777
|
-
const out = [];
|
|
3778
|
-
for (const ch of s) {
|
|
3779
|
-
if (out.length && (JOINS.test(ch) || out[out.length - 1].endsWith(""))) out[out.length - 1] += ch;
|
|
3780
|
-
else out.push(ch);
|
|
3781
|
-
}
|
|
3782
|
-
return out;
|
|
3783
|
-
}
|
|
3784
|
-
var graphemes = (s) => clusters(s);
|
|
3785
|
-
var SA = /[\u0e00-\u0eff\u1000-\u109f\u1780-\u17ff\u19e0-\u19ff\u1a20-\u1aaf\ua9e0-\ua9ff\uaa60-\uaadf]/;
|
|
3786
|
-
var WORDS = INTL ? new Intl.Segmenter(void 0, { granularity: "word" }) : null;
|
|
3787
|
-
function saBreaks(text, segmenter = WORDS) {
|
|
3788
|
-
const out = /* @__PURE__ */ new Set();
|
|
3789
|
-
if (!segmenter) return out;
|
|
3790
|
-
for (const { index } of segmenter.segment(text)) {
|
|
3791
|
-
if (index > 0 && (SA.test(text[index]) || SA.test(text[index - 1]))) out.add(index);
|
|
3792
|
-
}
|
|
3793
|
-
return out;
|
|
3794
|
-
}
|
|
3795
|
-
var TextMeasurer = class {
|
|
3796
|
-
/** @param {import('./fonts.js').FontStore} store */
|
|
3797
|
-
constructor(store) {
|
|
3798
|
-
this.store = store;
|
|
3799
|
-
this.faces = /* @__PURE__ */ new Map();
|
|
3800
|
-
this.boxes = /* @__PURE__ */ new WeakMap();
|
|
3801
|
-
this.boxCount = 0;
|
|
3802
|
-
this.looks = /* @__PURE__ */ new WeakMap();
|
|
3803
|
-
this.cover = /* @__PURE__ */ new Map();
|
|
3804
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
3805
|
-
this.missing = /* @__PURE__ */ new Set();
|
|
3806
|
-
this.noGlyph = /* @__PURE__ */ new Set();
|
|
3807
|
-
}
|
|
3808
|
-
/**
|
|
3809
|
-
* The key to measure with: `key` when loaded; else (not loaded yet, or failed) the bundled face of the same weight
|
|
3810
|
-
* and style when that is loaded, else the core regular face. A key not yet loaded is asked for in the next pass.
|
|
3811
|
-
*/
|
|
3812
|
-
usable(key) {
|
|
3813
|
-
if (this.store.has(key)) return key;
|
|
3814
|
-
if (!this.store.failed.has(key)) this.missing.add(key);
|
|
3815
|
-
const c = fontCss(key);
|
|
3816
|
-
const same2 = resolveFontKey(DEFAULT_FAMILY, c.weight, c.style);
|
|
3817
|
-
return this.store.has(same2) ? same2 : CORE_FONT_KEY;
|
|
3818
|
-
}
|
|
3819
|
-
/**
|
|
3820
|
-
* A loaded face's numbers, read once: fontkit's getters (unitsPerEm, ascent…) decode tables on every read.
|
|
3821
|
-
* adv: advances per code point (-1 = not on the fast path); words: fast-path units per string (a table repeats them).
|
|
3822
|
-
* @param {string} key a loaded key
|
|
3823
|
-
*/
|
|
3824
|
-
face(key) {
|
|
3825
|
-
let f4 = this.faces.get(key);
|
|
3826
|
-
if (!f4) {
|
|
3827
|
-
const font = this.store.get(key);
|
|
3828
|
-
f4 = { font, upm: font.unitsPerEm, ascent: font.ascent, descent: font.descent, adv: /* @__PURE__ */ new Map(), words: /* @__PURE__ */ new Map(), met: /* @__PURE__ */ new Map() };
|
|
3829
|
-
this.faces.set(key, f4);
|
|
3830
|
-
}
|
|
3831
|
-
return f4;
|
|
3832
|
-
}
|
|
3833
|
-
/** Font units of `text` on the fast path, or -1 when it needs the complex path. */
|
|
3834
|
-
fast(text, key) {
|
|
3835
|
-
const f4 = this.face(key);
|
|
3836
|
-
const hit = f4.words.get(text);
|
|
3837
|
-
if (hit !== void 0) return hit;
|
|
3838
|
-
const m = f4.adv, font = f4.font;
|
|
3839
|
-
let units = 0;
|
|
3840
|
-
for (let i = 0; i < text.length; i++) {
|
|
3841
|
-
let cp = text.charCodeAt(i);
|
|
3842
|
-
if (cp >= 55296 && cp <= 56319) {
|
|
3843
|
-
const lo = text.charCodeAt(i + 1);
|
|
3844
|
-
if (lo >= 56320 && lo <= 57343) {
|
|
3845
|
-
cp = (cp - 55296) * 1024 + lo - 56320 + 65536;
|
|
3846
|
-
i++;
|
|
3847
|
-
}
|
|
3848
|
-
}
|
|
3849
|
-
let a = m.get(cp);
|
|
3850
|
-
if (a === void 0) {
|
|
3851
|
-
a = special(cp) || cp >= 32 && !font.hasGlyphForCodePoint(cp) ? -1 : font.glyphForCodePoint(cp).advanceWidth || 0;
|
|
3852
|
-
m.set(cp, a);
|
|
3853
|
-
}
|
|
3854
|
-
if (a < 0) {
|
|
3855
|
-
units = -1;
|
|
3856
|
-
break;
|
|
3857
|
-
}
|
|
3858
|
-
units += a;
|
|
3859
|
-
}
|
|
3860
|
-
if (text.length <= 256) {
|
|
3861
|
-
if (f4.words.size >= 5e4) f4.words.clear();
|
|
3862
|
-
f4.words.set(text, units);
|
|
3863
|
-
}
|
|
3864
|
-
return units;
|
|
3865
|
-
}
|
|
3866
|
-
/**
|
|
3867
|
-
* Advance width in points. @param {string} text @param {string} key @param {number} size
|
|
3868
|
-
* @param {'ltr'|'rtl'} [dir] paragraph direction (default: from the first strong character)
|
|
3869
|
-
*/
|
|
3870
|
-
width(text, key, size, dir) {
|
|
3871
|
-
const k = this.store.has(key) ? key : this.usable(key);
|
|
3872
|
-
const u = this.fast(text, k);
|
|
3873
|
-
if (u >= 0) return u * size / this.face(k).upm;
|
|
3874
|
-
return this.layout(text, k, dir).w * size;
|
|
3875
|
-
}
|
|
3876
|
-
/**
|
|
3877
|
-
* Where underline and strike-through go, from the font's own tables (post and OS/2), in points:
|
|
3878
|
-
* y offsets of the stroke centre from the baseline (positive = down, as on the page) and thicknesses.
|
|
3879
|
-
* A font without the tables falls back to proportions of its descent and x-height.
|
|
3880
|
-
* @param {string} key @param {number} size
|
|
3881
|
-
*/
|
|
3882
|
-
decoration(key, size) {
|
|
3883
|
-
const f4 = this.store.get(this.usable(key));
|
|
3884
|
-
const u = f4.unitsPerEm, os2 = f4["OS/2"] || {};
|
|
3885
|
-
const ut = f4.underlineThickness > 0 ? f4.underlineThickness : u / 14;
|
|
3886
|
-
const up = f4.underlinePosition < 0 ? f4.underlinePosition : f4.descent / 2;
|
|
3887
|
-
const st = os2.yStrikeoutSize > 0 ? os2.yStrikeoutSize : ut;
|
|
3888
|
-
const sp = os2.yStrikeoutPosition > 0 ? os2.yStrikeoutPosition : (f4.xHeight > 0 ? f4.xHeight : f4.ascent * 0.5) / 2 + st / 2;
|
|
3889
|
-
const k = size / u;
|
|
3890
|
-
return { underlineY: -(up - ut / 2) * k, underlineWidth: ut * k, strikeY: -(sp - st / 2) * k, strikeWidth: st * k };
|
|
3891
|
-
}
|
|
3892
|
-
/**
|
|
3893
|
-
* Superscript and subscript from the font's OS/2 table, in em: the size of the small text and how far its
|
|
3894
|
-
* baseline moves (rise up for superscript, drop down for subscript). Fonts without the values get the
|
|
3895
|
-
* proportions most fonts use.
|
|
3896
|
-
* @param {string} key
|
|
3897
|
-
*/
|
|
3898
|
-
script(key) {
|
|
3899
|
-
const f4 = this.store.get(this.usable(key));
|
|
3900
|
-
const u = f4.unitsPerEm, o = f4["OS/2"] || {};
|
|
3901
|
-
const frac = (v, d) => v > 0 && v < u * 2 ? v / u : d;
|
|
3902
|
-
return { supScale: frac(o.ySuperscriptYSize, 0.65), supRise: frac(o.ySuperscriptYOffset, 0.35), subScale: frac(o.ySubscriptYSize, 0.65), subDrop: frac(o.ySubscriptYOffset, 0.15) };
|
|
3903
|
-
}
|
|
3904
|
-
/** @param {string} key @param {number} size */
|
|
3905
|
-
metrics(key, size) {
|
|
3906
|
-
const f4 = this.face(this.usable(key));
|
|
3907
|
-
let r = f4.met.get(size);
|
|
3908
|
-
if (!r) {
|
|
3909
|
-
r = Object.freeze({ ascent: f4.ascent * size / f4.upm, descent: -f4.descent * size / f4.upm });
|
|
3910
|
-
f4.met.set(size, r);
|
|
3911
|
-
}
|
|
3912
|
-
return r;
|
|
3913
|
-
}
|
|
3914
|
-
/**
|
|
3915
|
-
* The visually ordered runs of one line, or null when the line is simple (draw `text` as it is).
|
|
3916
|
-
* @param {string} text @param {string} key @param {number} size @param {'ltr'|'rtl'} [dir]
|
|
3917
|
-
* @returns {Run[]|null}
|
|
3918
|
-
*/
|
|
3919
|
-
runs(text, key, size, dir) {
|
|
3920
|
-
if (!dir && !MAYBE.test(text)) return null;
|
|
3921
|
-
const k = this.usable(key);
|
|
3922
|
-
if (this.fast(text, k) >= 0) return null;
|
|
3923
|
-
return this.layout(text, k, dir).runs.map((r) => ({
|
|
3924
|
-
...r,
|
|
3925
|
-
x: r.x * size,
|
|
3926
|
-
w: r.w * size,
|
|
3927
|
-
glyphs: r.glyphs.map((v, i) => i % 4 === 1 || i % 4 === 2 ? v * size : v)
|
|
3928
|
-
}));
|
|
3929
|
-
}
|
|
3930
|
-
/** Paragraph direction of text that needs shaping, bidi or CJK breaks, else undefined. */
|
|
3931
|
-
direction(text, key) {
|
|
3932
|
-
if (!MAYBE.test(text)) return void 0;
|
|
3933
|
-
const k = this.usable(key);
|
|
3934
|
-
if (this.fast(text, k) >= 0) return void 0;
|
|
3935
|
-
return this.layout(text, k, void 0).rtl ? "rtl" : "ltr";
|
|
3936
|
-
}
|
|
3937
|
-
/**
|
|
3938
|
-
* Greedy line breaking. Simple paragraphs break at spaces (a word longer than the line breaks by
|
|
3939
|
-
* character), exactly as before. Others also break between CJK characters (kinsoku applied), never
|
|
3940
|
-
* inside a grapheme cluster (a conjunct, a letter and its marks), and remember their direction.
|
|
3941
|
-
* @param {string} text @param {string} key @param {number} size @param {number} maxWidth
|
|
3942
|
-
* @returns {{text: string, width: number, dir?: 'ltr'|'rtl'}[]}
|
|
3943
|
-
*/
|
|
3944
|
-
wrap(text, key, size, maxWidth) {
|
|
3945
|
-
const out = [];
|
|
3946
|
-
const k = this.usable(key);
|
|
3947
|
-
const sp = this.width(" ", k, size);
|
|
3948
|
-
const str = String(text);
|
|
3949
|
-
for (const para of str.includes("\n") ? str.split(/\r?\n/) : [str]) {
|
|
3950
|
-
const dir = this.direction(para, k);
|
|
3951
|
-
if (dir) {
|
|
3952
|
-
this.wrapComplex(para, k, size, maxWidth, dir, sp, out);
|
|
3953
|
-
continue;
|
|
3954
|
-
}
|
|
3955
|
-
if (!para.includes(" ")) {
|
|
3956
|
-
const ww = this.width(para, k, size);
|
|
3957
|
-
if (ww <= maxWidth || maxWidth <= 0) {
|
|
3958
|
-
out.push({ text: para, width: ww, end: true });
|
|
3959
|
-
continue;
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
const words = para.split(/ +/);
|
|
3963
|
-
let line2 = "", lw = 0;
|
|
3964
|
-
const push = () => {
|
|
3965
|
-
out.push({ text: line2, width: lw });
|
|
3966
|
-
line2 = "";
|
|
3967
|
-
lw = 0;
|
|
3968
|
-
};
|
|
3969
|
-
for (let wi = 0; wi < words.length; wi++) {
|
|
3970
|
-
const w = words[wi];
|
|
3971
|
-
if (w === "" && wi > 0) continue;
|
|
3972
|
-
const ww = this.width(w, k, size);
|
|
3973
|
-
if (line2 === "") {
|
|
3974
|
-
if (ww <= maxWidth || maxWidth <= 0) {
|
|
3975
|
-
line2 = w;
|
|
3976
|
-
lw = ww;
|
|
3977
|
-
continue;
|
|
3978
|
-
}
|
|
3979
|
-
} else if (lw + sp + ww <= maxWidth) {
|
|
3980
|
-
line2 += " " + w;
|
|
3981
|
-
lw += sp + ww;
|
|
3982
|
-
continue;
|
|
3983
|
-
} else {
|
|
3984
|
-
push();
|
|
3985
|
-
if (ww <= maxWidth) {
|
|
3986
|
-
line2 = w;
|
|
3987
|
-
lw = ww;
|
|
3988
|
-
continue;
|
|
3989
|
-
}
|
|
3990
|
-
}
|
|
3991
|
-
let after = -1;
|
|
3992
|
-
for (const ch of w) {
|
|
3993
|
-
const cw = this.width(ch, k, size);
|
|
3994
|
-
if (lw + cw > maxWidth && line2 !== "") {
|
|
3995
|
-
if (after > 0 && after < line2.length) {
|
|
3996
|
-
const rest = line2.slice(after);
|
|
3997
|
-
line2 = line2.slice(0, after);
|
|
3998
|
-
lw = this.width(line2, k, size);
|
|
3999
|
-
push();
|
|
4000
|
-
line2 = rest;
|
|
4001
|
-
lw = this.width(rest, k, size);
|
|
4002
|
-
} else push();
|
|
4003
|
-
after = -1;
|
|
4004
|
-
}
|
|
4005
|
-
line2 += ch;
|
|
4006
|
-
lw += cw;
|
|
4007
|
-
if ((ch === "/" || ch === "-") && line2.length > 1) after = line2.length;
|
|
4008
|
-
}
|
|
4009
|
-
}
|
|
4010
|
-
push();
|
|
4011
|
-
out[out.length - 1].end = true;
|
|
4012
|
-
}
|
|
4013
|
-
return out;
|
|
4014
|
-
}
|
|
4015
|
-
/** @param {string} para @param {string} key @param {number} size @param {number} maxWidth @param {'ltr'|'rtl'} dir @param {number} sp @param {any[]} out */
|
|
4016
|
-
wrapComplex(para, key, size, maxWidth, dir, sp, out) {
|
|
4017
|
-
const tokens = [];
|
|
4018
|
-
const sa = SA.test(para) ? saBreaks(para) : null;
|
|
4019
|
-
let cur = "", space = false, prev = "", at = 0;
|
|
4020
|
-
for (const ch of para) {
|
|
4021
|
-
const i = at;
|
|
4022
|
-
at += ch.length;
|
|
4023
|
-
if (ch === " ") {
|
|
4024
|
-
if (cur) {
|
|
4025
|
-
tokens.push({ text: cur, space });
|
|
4026
|
-
cur = "";
|
|
4027
|
-
}
|
|
4028
|
-
space = tokens.length > 0;
|
|
4029
|
-
prev = "";
|
|
4030
|
-
continue;
|
|
4031
|
-
}
|
|
4032
|
-
const cp = (
|
|
4033
|
-
/** @type {number} */
|
|
4034
|
-
ch.codePointAt(0)
|
|
4035
|
-
);
|
|
4036
|
-
const pcp = prev ? (
|
|
4037
|
-
/** @type {number} */
|
|
4038
|
-
prev.codePointAt(0)
|
|
4039
|
-
) : 0;
|
|
4040
|
-
if (cur && (sa?.has(i) || (cjk(cp) || cjk(pcp)) && !NO_START.has(ch) && !NO_END.has(prev) && !STICKY.test(ch))) {
|
|
4041
|
-
tokens.push({ text: cur, space });
|
|
4042
|
-
cur = "";
|
|
4043
|
-
space = false;
|
|
4044
|
-
}
|
|
4045
|
-
cur += ch;
|
|
4046
|
-
prev = ch;
|
|
4047
|
-
}
|
|
4048
|
-
if (cur) tokens.push({ text: cur, space });
|
|
4049
|
-
let line2 = "", lw = 0;
|
|
4050
|
-
const push = () => {
|
|
4051
|
-
out.push({ text: line2, width: this.width(line2, key, size, dir), dir });
|
|
4052
|
-
line2 = "";
|
|
4053
|
-
lw = 0;
|
|
4054
|
-
};
|
|
4055
|
-
for (const t of tokens) {
|
|
4056
|
-
const tw = this.width(t.text, key, size, dir);
|
|
4057
|
-
const gap = line2 && t.space ? sp : 0;
|
|
4058
|
-
if (line2 === "" ? tw <= maxWidth || maxWidth <= 0 : lw + gap + tw <= maxWidth) {
|
|
4059
|
-
line2 += (line2 && t.space ? " " : "") + t.text;
|
|
4060
|
-
lw += gap + tw;
|
|
4061
|
-
continue;
|
|
4062
|
-
}
|
|
4063
|
-
if (line2) push();
|
|
4064
|
-
if (tw <= maxWidth) {
|
|
4065
|
-
line2 = t.text;
|
|
4066
|
-
lw = tw;
|
|
4067
|
-
continue;
|
|
4068
|
-
}
|
|
4069
|
-
for (const g of graphemes(t.text)) {
|
|
4070
|
-
const gw = this.width(g, key, size, dir);
|
|
4071
|
-
if (lw + gw > maxWidth && line2 !== "") push();
|
|
4072
|
-
line2 += g;
|
|
4073
|
-
lw += gw;
|
|
4074
|
-
}
|
|
4075
|
-
}
|
|
4076
|
-
push();
|
|
4077
|
-
}
|
|
4078
|
-
/** Cut a line so it fits, then add "…". */
|
|
4079
|
-
ellipsize(text, key, size, maxWidth, dir) {
|
|
4080
|
-
const ell = "…";
|
|
4081
|
-
if (this.width(text, key, size, dir) <= maxWidth) return text;
|
|
4082
|
-
const ew = this.width(ell, key, size);
|
|
4083
|
-
if (this.direction(text, key)) {
|
|
4084
|
-
const gs = graphemes(text);
|
|
4085
|
-
while (gs.length && this.width(gs.join(""), key, size, dir) + ew > maxWidth) gs.pop();
|
|
4086
|
-
return gs.join("").trimEnd() + ell;
|
|
4087
|
-
}
|
|
4088
|
-
let s = text;
|
|
4089
|
-
while (s.length && this.width(s, key, size) + ew > maxWidth) s = s.slice(0, -1);
|
|
4090
|
-
return s.trimEnd() + ell;
|
|
4091
|
-
}
|
|
4092
|
-
/** Does this font have a glyph for the character? */
|
|
4093
|
-
has(key, cp) {
|
|
4094
|
-
let m = this.cover.get(key);
|
|
4095
|
-
if (!m) {
|
|
4096
|
-
m = /* @__PURE__ */ new Map();
|
|
4097
|
-
this.cover.set(key, m);
|
|
4098
|
-
}
|
|
4099
|
-
let h = m.get(cp);
|
|
4100
|
-
if (h === void 0) {
|
|
4101
|
-
h = !!this.store.get(key).hasGlyphForCodePoint(cp);
|
|
4102
|
-
m.set(cp, h);
|
|
4103
|
-
}
|
|
4104
|
-
return h;
|
|
4105
|
-
}
|
|
4106
|
-
/** The font for one character: the chosen one, else the bundled fallback for its script, else the one before. */
|
|
4107
|
-
pick(cp, key, prev) {
|
|
4108
|
-
if (cp < 32 || this.has(key, cp)) return key;
|
|
4109
|
-
const fb = fallbackKey(cp, key);
|
|
4110
|
-
if (fb && !this.store.failed.has(fb)) {
|
|
4111
|
-
if (!this.store.has(fb)) {
|
|
4112
|
-
this.missing.add(fb);
|
|
4113
|
-
return key;
|
|
4114
|
-
}
|
|
4115
|
-
if (this.has(fb, cp)) return fb;
|
|
4116
|
-
}
|
|
4117
|
-
if (prev && prev !== key && this.has(prev, cp)) return prev;
|
|
4118
|
-
if (!this.missing.size) this.noGlyph.add(String.fromCodePoint(cp));
|
|
4119
|
-
return key;
|
|
4120
|
-
}
|
|
4121
|
-
/**
|
|
4122
|
-
* Bidi levels, a font per character, runs (one font, level, script, and space or not), shaping,
|
|
4123
|
-
* then visual order. Cached at size 1.
|
|
4124
|
-
* @param {string} text @param {string} key a loaded key @param {'ltr'|'rtl'|undefined} dir
|
|
4125
|
-
* @returns {{ w: number, rtl: boolean, runs: Run[] }}
|
|
4126
|
-
*/
|
|
4127
|
-
layout(text, key, dir) {
|
|
4128
|
-
const ck = `${key}${dir || ""}${text}`;
|
|
4129
|
-
const hit = this.cache.get(ck);
|
|
4130
|
-
if (hit) return hit;
|
|
4131
|
-
let levels = null, rtl = dir === "rtl";
|
|
4132
|
-
if (dir === "rtl" || RTL_OR_CONTROLS.test(text)) {
|
|
4133
|
-
const e = bidi.getEmbeddingLevels(text, dir);
|
|
4134
|
-
levels = e.levels;
|
|
4135
|
-
rtl = ((e.paragraphs[0]?.level ?? 0) & 1) === 1;
|
|
4136
|
-
}
|
|
4137
|
-
const segs = [];
|
|
4138
|
-
let cur = null, prevFont = "";
|
|
4139
|
-
for (let i = 0; i < text.length; ) {
|
|
4140
|
-
const cp = (
|
|
4141
|
-
/** @type {number} */
|
|
4142
|
-
text.codePointAt(i)
|
|
4143
|
-
);
|
|
4144
|
-
const n = cp > 65535 ? 2 : 1;
|
|
4145
|
-
const ch = text.slice(i, i + n);
|
|
4146
|
-
const font = STICKY.test(ch) && prevFont ? prevFont : this.pick(cp, key, prevFont);
|
|
4147
|
-
const level = levels ? levels[i] : 0;
|
|
4148
|
-
const ws = SPACE.test(ch);
|
|
4149
|
-
const script = scriptOf(cp);
|
|
4150
|
-
if (!cur || font !== cur.font || level !== cur.level || ws !== cur.ws || script !== "Common" && cur.script !== "Common" && script !== cur.script) {
|
|
4151
|
-
cur = { start: i, end: i + n, font, level, script, ws };
|
|
4152
|
-
segs.push(cur);
|
|
4153
|
-
} else {
|
|
4154
|
-
cur.end = i + n;
|
|
4155
|
-
if (cur.script === "Common") cur.script = script;
|
|
4156
|
-
}
|
|
4157
|
-
prevFont = font;
|
|
4158
|
-
i += n;
|
|
4159
|
-
}
|
|
4160
|
-
const runs = segs.map((s) => this.shapeRun(text.slice(s.start, s.end), s.font, (s.level & 1) === 1, s.script));
|
|
4161
|
-
const order = runs.map((r, i) => ({ r, level: segs[i].level }));
|
|
4162
|
-
let max = 0, minOdd = Infinity;
|
|
4163
|
-
for (const o of order) {
|
|
4164
|
-
max = Math.max(max, o.level);
|
|
4165
|
-
if (o.level & 1) minOdd = Math.min(minOdd, o.level);
|
|
4166
|
-
}
|
|
4167
|
-
for (let l = max; l >= minOdd; l--) {
|
|
4168
|
-
for (let i = 0; i < order.length; ) {
|
|
4169
|
-
if (order[i].level < l) {
|
|
4170
|
-
i++;
|
|
4171
|
-
continue;
|
|
4172
|
-
}
|
|
4173
|
-
let j = i;
|
|
4174
|
-
while (j < order.length && order[j].level >= l) j++;
|
|
4175
|
-
const rev = order.slice(i, j).reverse();
|
|
4176
|
-
order.splice(i, j - i, ...rev);
|
|
4177
|
-
i = j;
|
|
4178
|
-
}
|
|
4179
|
-
}
|
|
4180
|
-
let x = 0;
|
|
4181
|
-
for (const o of order) {
|
|
4182
|
-
o.r.x = x;
|
|
4183
|
-
x += o.r.w;
|
|
4184
|
-
}
|
|
4185
|
-
const out = { w: x, rtl, runs: order.map((o) => o.r) };
|
|
4186
|
-
if (this.cache.size > 2e4) this.cache.clear();
|
|
4187
|
-
this.cache.set(ck, out);
|
|
4188
|
-
return out;
|
|
4189
|
-
}
|
|
4190
|
-
/** One run at size 1: HarfBuzz for complex scripts and right-to-left text, else character-map glyphs. @returns {Run} */
|
|
4191
|
-
shapeRun(text, key, rtl, script) {
|
|
4192
|
-
const complex = !SIMPLE.has(script);
|
|
4193
|
-
const store = this.store;
|
|
4194
|
-
const glyphs = [];
|
|
4195
|
-
let pen = 0;
|
|
4196
|
-
if ((complex || rtl) && store.shaper) {
|
|
4197
|
-
const hf = store.hbFont(key);
|
|
4198
|
-
const g = store.shaper.shape(hf, text, rtl, !complex);
|
|
4199
|
-
for (let i = 0; i < g.length; i += 5) {
|
|
4200
|
-
glyphs.push(g[i], (pen + g[i + 2]) / hf.upem, g[i + 3] / hf.upem, g[i + 4]);
|
|
4201
|
-
pen += g[i + 1];
|
|
4202
|
-
}
|
|
4203
|
-
return { font: key, x: 0, w: pen / hf.upem, text, ...rtl ? { rtl } : {}, ...complex ? { shaped: true } : {}, glyphs };
|
|
4204
|
-
}
|
|
4205
|
-
if ((complex || rtl) && !store.failed.has(SHAPER)) this.missing.add(SHAPER);
|
|
4206
|
-
else if (complex || rtl) this.unshaped = true;
|
|
4207
|
-
const f4 = store.get(key);
|
|
4208
|
-
const list = [];
|
|
4209
|
-
for (let i = 0; i < text.length; ) {
|
|
4210
|
-
const cp = (
|
|
4211
|
-
/** @type {number} */
|
|
4212
|
-
text.codePointAt(i)
|
|
4213
|
-
);
|
|
4214
|
-
const g = f4.glyphForCodePoint(cp);
|
|
4215
|
-
list.push([g.id, g.advanceWidth || 0, i]);
|
|
4216
|
-
i += cp > 65535 ? 2 : 1;
|
|
4217
|
-
}
|
|
4218
|
-
if (rtl) list.reverse();
|
|
4219
|
-
for (const [id, a, c] of list) {
|
|
4220
|
-
glyphs.push(id, pen / f4.unitsPerEm, 0, c);
|
|
4221
|
-
pen += a;
|
|
4222
|
-
}
|
|
4223
|
-
return { font: key, x: 0, w: pen / f4.unitsPerEm, text, ...rtl ? { rtl } : {}, glyphs };
|
|
4224
|
-
}
|
|
4225
|
-
};
|
|
4226
|
-
function finishText(items, m) {
|
|
4227
|
-
for (const it of items) {
|
|
4228
|
-
if (it.t === "field") {
|
|
4229
|
-
finishText(it.draw, m);
|
|
4230
|
-
continue;
|
|
4231
|
-
}
|
|
4232
|
-
if (it.t !== "text") continue;
|
|
4233
|
-
it.font = m.usable(it.font);
|
|
4234
|
-
for (const l of it.lines) {
|
|
4235
|
-
const r = m.runs(l.text, it.font, it.size, l.dir);
|
|
4236
|
-
if (r) l.runs = r;
|
|
4237
|
-
}
|
|
4238
|
-
}
|
|
4239
|
-
}
|
|
4240
|
-
|
|
4241
3743
|
// src/engine/style.js
|
|
4242
3744
|
var BASE_STYLE = {
|
|
4243
3745
|
fontFamily: "Inter",
|
|
@@ -4669,6 +4171,11 @@ function niceScale(min, max, ticks = 5) {
|
|
|
4669
4171
|
const step3 = niceNum(range / Math.max(1, ticks - 1), true);
|
|
4670
4172
|
return { lo: Math.floor(min / step3) * step3, hi: Math.ceil(max / step3) * step3, step: step3 };
|
|
4671
4173
|
}
|
|
4174
|
+
function niceScaleSpan(min, max, ticks = 5) {
|
|
4175
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || !(max > min)) return niceScale(min, max, ticks);
|
|
4176
|
+
const step3 = niceNum((max - min) / Math.max(1, ticks - 1), true);
|
|
4177
|
+
return { lo: Math.floor(min / step3) * step3, hi: Math.ceil(max / step3) * step3, step: step3 };
|
|
4178
|
+
}
|
|
4672
4179
|
function niceNum(x, round) {
|
|
4673
4180
|
const e = Math.floor(Math.log10(x));
|
|
4674
4181
|
const f4 = x / 10 ** e;
|
|
@@ -5625,7 +5132,7 @@ var byName2 = (lc, pal) => (name) => {
|
|
|
5625
5132
|
};
|
|
5626
5133
|
function chartData(item, ctx, lc, pal = paletteOf(item.palette)) {
|
|
5627
5134
|
let rows = item.dataSet ? related(lc.dataSets[item.dataSet] || [], ctx) : ctx.aggRows || [];
|
|
5628
|
-
if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name}: the data set "${item.dataSet}" does not exist`);
|
|
5135
|
+
if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
|
|
5629
5136
|
if (item.filters?.length) rows = applyFilters(rows, item.filters, ctx);
|
|
5630
5137
|
if (item.sort?.length) rows = applySort(rows, item.sort, ctx);
|
|
5631
5138
|
const who = item.name || "Chart";
|
|
@@ -5913,13 +5420,15 @@ function valueAxis(spec, lo, hi, minPos, { includeZero = true, len = 100, fs = 7
|
|
|
5913
5420
|
lo = lo < 0 ? -1 : 0;
|
|
5914
5421
|
hi = 1;
|
|
5915
5422
|
}
|
|
5423
|
+
let padded = false;
|
|
5916
5424
|
if (pad2 > 0 && hi > lo) {
|
|
5917
5425
|
const d = (hi - lo) * pad2;
|
|
5918
|
-
if (smin == null) lo
|
|
5919
|
-
if (smax == null) hi
|
|
5426
|
+
if (smin == null) lo = lo >= 0 && lo - d < 0 ? 0 : lo - d;
|
|
5427
|
+
if (smax == null) hi = hi <= 0 && hi + d > 0 ? 0 : hi + d;
|
|
5428
|
+
padded = true;
|
|
5920
5429
|
}
|
|
5921
5430
|
const want = len < fs * 6 ? 3 : 5;
|
|
5922
|
-
const sc = niceScale(smin ?? lo, smax ?? hi, want);
|
|
5431
|
+
const sc = padded && smin == null && smax == null && (lo >= 0 || hi <= 0) ? niceScaleSpan(lo, hi, want) : niceScale(smin ?? lo, smax ?? hi, want);
|
|
5923
5432
|
let a = sc.lo, b = sc.hi, ticks;
|
|
5924
5433
|
if (smin != null || smax != null) {
|
|
5925
5434
|
a = smin ?? sc.lo;
|
|
@@ -6347,31 +5856,27 @@ function rowPoints(type, g) {
|
|
|
6347
5856
|
rows = rows.slice(0, cap);
|
|
6348
5857
|
}
|
|
6349
5858
|
const blank2 = (v) => v == null || v === "";
|
|
6350
|
-
const
|
|
5859
|
+
const drawn = /* @__PURE__ */ new Set();
|
|
6351
5860
|
const sets = data.series.map((s, si) => {
|
|
6352
5861
|
const pts = [];
|
|
6353
5862
|
rows.forEach((r, ri) => {
|
|
6354
5863
|
c.fields = r;
|
|
6355
5864
|
const rx = safe2(item.xValue, c, lc, who), ry = safe2(s.def.value || "=0", { ...c, aggRows: [r] }, lc, who);
|
|
6356
|
-
if (blank2(rx) || blank2(ry))
|
|
6357
|
-
missing.add(ri);
|
|
6358
|
-
return;
|
|
6359
|
-
}
|
|
5865
|
+
if (blank2(rx) || blank2(ry)) return;
|
|
6360
5866
|
const x = Number(rx), y = Number(ry);
|
|
6361
|
-
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
6362
|
-
missing.add(ri);
|
|
6363
|
-
return;
|
|
6364
|
-
}
|
|
5867
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return;
|
|
6365
5868
|
let size = 1;
|
|
6366
5869
|
if (type === "bubble") {
|
|
6367
5870
|
size = Number(safe2(s.def.size || "=1", { ...c, aggRows: [r] }, lc, who));
|
|
6368
5871
|
if (!Number.isFinite(size) || size <= 0) return;
|
|
6369
5872
|
}
|
|
6370
5873
|
pts.push({ x, y, size });
|
|
5874
|
+
drawn.add(ri);
|
|
6371
5875
|
});
|
|
6372
5876
|
return { s, pts, i: si };
|
|
6373
5877
|
});
|
|
6374
|
-
|
|
5878
|
+
const missing = rows.length - drawn.size;
|
|
5879
|
+
if (missing > 0) lc.warn(`${who}: ${missing} of ${rows.length} rows have no x or y value and are not drawn`);
|
|
6375
5880
|
return sets;
|
|
6376
5881
|
}
|
|
6377
5882
|
function paintXY(type, g) {
|
|
@@ -7652,12 +7157,12 @@ function parseRich(html, opt = {}) {
|
|
|
7652
7157
|
i = e + 1;
|
|
7653
7158
|
continue;
|
|
7654
7159
|
}
|
|
7655
|
-
const
|
|
7656
|
-
if (!
|
|
7657
|
-
i =
|
|
7658
|
-
const name =
|
|
7160
|
+
const tag3 = readTag(s, i);
|
|
7161
|
+
if (!tag3) break;
|
|
7162
|
+
i = tag3.end;
|
|
7163
|
+
const name = tag3.name;
|
|
7659
7164
|
if (!name) continue;
|
|
7660
|
-
if (!
|
|
7165
|
+
if (!tag3.close && SKIP.has(name)) {
|
|
7661
7166
|
const e = low.indexOf(`</${name}`, i);
|
|
7662
7167
|
if (e < 0) break;
|
|
7663
7168
|
const g = s.indexOf(">", e);
|
|
@@ -7666,7 +7171,7 @@ function parseRich(html, opt = {}) {
|
|
|
7666
7171
|
continue;
|
|
7667
7172
|
}
|
|
7668
7173
|
if (name === "br") {
|
|
7669
|
-
if (
|
|
7174
|
+
if (tag3.close) continue;
|
|
7670
7175
|
if (++nBreaks > RICH_LIMITS.maxBreaks) {
|
|
7671
7176
|
cutAt(`${RICH_LIMITS.maxBreaks} line breaks`);
|
|
7672
7177
|
continue;
|
|
@@ -7679,7 +7184,7 @@ function parseRich(html, opt = {}) {
|
|
|
7679
7184
|
if (unknown.size < 20) unknown.add(name.slice(0, 20));
|
|
7680
7185
|
continue;
|
|
7681
7186
|
}
|
|
7682
|
-
if (
|
|
7187
|
+
if (tag3.close) {
|
|
7683
7188
|
if (ignored > 0) {
|
|
7684
7189
|
ignored--;
|
|
7685
7190
|
continue;
|
|
@@ -7697,7 +7202,7 @@ function parseRich(html, opt = {}) {
|
|
|
7697
7202
|
stack2.length = k;
|
|
7698
7203
|
continue;
|
|
7699
7204
|
}
|
|
7700
|
-
if (
|
|
7205
|
+
if (tag3.self && name !== "li") continue;
|
|
7701
7206
|
if (stack2.length > RICH_LIMITS.maxDepth) {
|
|
7702
7207
|
ignored++;
|
|
7703
7208
|
if (!deepWarned) {
|
|
@@ -7731,7 +7236,7 @@ function parseRich(html, opt = {}) {
|
|
|
7731
7236
|
st.script = "sub";
|
|
7732
7237
|
break;
|
|
7733
7238
|
case "a": {
|
|
7734
|
-
const h = decode2(
|
|
7239
|
+
const h = decode2(tag3.attrs.href ?? "");
|
|
7735
7240
|
const ok = safeHref(h);
|
|
7736
7241
|
if (ok) st.href = ok;
|
|
7737
7242
|
else if (h) warn(`Rich text: the link "${h.slice(0, 40)}" is not allowed (only http, https, mailto and / links); its text is kept.`);
|
|
@@ -7743,7 +7248,7 @@ function parseRich(html, opt = {}) {
|
|
|
7743
7248
|
st.fontWeight = "bold";
|
|
7744
7249
|
}
|
|
7745
7250
|
}
|
|
7746
|
-
if (
|
|
7251
|
+
if (tag3.attrs.style) applyCss(tag3.attrs.style, st);
|
|
7747
7252
|
if (BLOCK.has(name)) {
|
|
7748
7253
|
flush();
|
|
7749
7254
|
if (name === "ul" || name === "ol") lists.push({ ordered: name === "ol", n: 0 });
|
|
@@ -8792,7 +8297,7 @@ function hidden(item, ctx, lc) {
|
|
|
8792
8297
|
const r = evalSafe(item.hidden, ctx, lc, item.name || item.type);
|
|
8793
8298
|
h = !r.err && truthy(r.v);
|
|
8794
8299
|
}
|
|
8795
|
-
return item.name && lc.state?.toggles?.[`item:${item.name}`] ? !h : h;
|
|
8300
|
+
return item.name && lc.state?.toggles?.[`item:${item.name || item.type}`] ? !h : h;
|
|
8796
8301
|
}
|
|
8797
8302
|
var SHRINK_DEFAULTS = { minFontSize: 4, step: 0.5 };
|
|
8798
8303
|
function rotationOf(item) {
|
|
@@ -9128,7 +8633,7 @@ register("table", {
|
|
|
9128
8633
|
else {
|
|
9129
8634
|
rows = item.dataSet && lc.dataSets[item.dataSet] || [];
|
|
9130
8635
|
rows = related(rows, tctx);
|
|
9131
|
-
if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name ||
|
|
8636
|
+
if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
|
|
9132
8637
|
if (item.filters?.length) rows = applyFilters(rows, item.filters, tctx);
|
|
9133
8638
|
const sort = lc.state?.sort?.[item.name] || item.sort;
|
|
9134
8639
|
if (sort?.length) rows = applySort(rows, sort, tctx);
|
|
@@ -9144,11 +8649,11 @@ register("table", {
|
|
|
9144
8649
|
if (overflow === "shrink" && room > 0) {
|
|
9145
8650
|
const k = room / designedW;
|
|
9146
8651
|
cols = cols.map((c) => ({ ...c, width: (Number(c.width) || 0) * k }));
|
|
9147
|
-
lc.warn(`${item.name ||
|
|
8652
|
+
lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they are narrowed to fit`);
|
|
9148
8653
|
} else if (overflow === "clip" || overflow === "paginate" && lc.fixed) {
|
|
9149
|
-
lc.warn(`${item.name ||
|
|
8654
|
+
lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so the columns past the page edge are cut`, true);
|
|
9150
8655
|
} else if (overflow === "paginate" && item.overflow !== "paginate") {
|
|
9151
|
-
lc.warn(`${item.name ||
|
|
8656
|
+
lc.warn(`${item.name || item.type}: its columns are ${Math.round(designedW)}pt wide, wider than the page (${Math.round(room)}pt), so they continue on the next pages`);
|
|
9152
8657
|
}
|
|
9153
8658
|
const colX = [];
|
|
9154
8659
|
let acc = item.x;
|
|
@@ -9158,6 +8663,7 @@ register("table", {
|
|
|
9158
8663
|
}
|
|
9159
8664
|
const width = acc - item.x;
|
|
9160
8665
|
const region = { name: item.name, columns: cols.map((c) => c.width), rows: [] };
|
|
8666
|
+
if (lc.tablesSeen) lc.tablesSeen.n++;
|
|
9161
8667
|
const tableScope = { rows, index: -1, run: [] };
|
|
9162
8668
|
const scopes = { ...tctx.scopes || {}, [item.name]: tableScope };
|
|
9163
8669
|
const groupChains = {};
|
|
@@ -9181,7 +8687,7 @@ register("table", {
|
|
|
9181
8687
|
const covered = (ci) => cover[ci] !== null;
|
|
9182
8688
|
const cellsAt = /* @__PURE__ */ new WeakMap();
|
|
9183
8689
|
const layRow = (row, ctx, kind, extra) => {
|
|
9184
|
-
if (row.hidden != null && row.hidden !== false && row.hidden !== "" && hidden({ hidden: row.hidden, name: `${item.name ||
|
|
8690
|
+
if (row.hidden != null && row.hidden !== false && row.hidden !== "" && hidden({ hidden: row.hidden, name: `${item.name || item.type} row` }, ctx, lc)) {
|
|
9185
8691
|
if (openSpans.length) {
|
|
9186
8692
|
for (const s of openSpans) s.left--;
|
|
9187
8693
|
for (const s of openSpans) if (s.left <= 0) closeSpan(s);
|
|
@@ -9215,7 +8721,7 @@ register("table", {
|
|
|
9215
8721
|
else try {
|
|
9216
8722
|
v = evalValue(cell.value ?? "", cctx);
|
|
9217
8723
|
} catch (e) {
|
|
9218
|
-
lc.warn(`${item.name ||
|
|
8724
|
+
lc.warn(`${item.name || item.type} cell: ${e.message}`);
|
|
9219
8725
|
v = "#Error";
|
|
9220
8726
|
}
|
|
9221
8727
|
let text = textOf(v, st, cctx, lc);
|
|
@@ -9258,7 +8764,7 @@ register("table", {
|
|
|
9258
8764
|
cells.forEach((c, k) => {
|
|
9259
8765
|
if (c.cell.visual?.type) {
|
|
9260
8766
|
items.push(...paintBox({ x: c.x, y: 0, w: c.w, h, st: c.st, tl: null, m: lc.m }));
|
|
9261
|
-
if (first) items.push(...paintVisual(c.cell.visual, { x: c.x, y: 0, w: c.w, h }, c.ctx, lc, c.st, `${item.name} cell`, { cell: true }));
|
|
8767
|
+
if (first) items.push(...paintVisual(c.cell.visual, { x: c.x, y: 0, w: c.w, h }, c.ctx, lc, c.st, `${item.name || item.type} cell`, { cell: true }));
|
|
9262
8768
|
return;
|
|
9263
8769
|
}
|
|
9264
8770
|
if (c.nested) {
|
|
@@ -9267,8 +8773,8 @@ register("table", {
|
|
|
9267
8773
|
else if (first) items.push(...c.nested.items);
|
|
9268
8774
|
return;
|
|
9269
8775
|
}
|
|
9270
|
-
const
|
|
9271
|
-
paintBoxAt(c.x, 0, c.w, h, c.st, lines ? { ...c.tl, lines: lines[k] } : c.tl, c.numeric, lc.m, true, items,
|
|
8776
|
+
const tag3 = lc.regions ? { table: item.name, row: rowId, col: colIdx[c.ci], span: c.span, kind, cols: colWidths, bg: c.st.backgroundColor || null } : null;
|
|
8777
|
+
paintBoxAt(c.x, 0, c.w, h, c.st, lines ? { ...c.tl, lines: lines[k] } : c.tl, c.numeric, lc.m, true, items, tag3);
|
|
9272
8778
|
if (first) cellLinks(items, c, h, kind, extra?.level);
|
|
9273
8779
|
});
|
|
9274
8780
|
if (first) {
|
|
@@ -9313,7 +8819,7 @@ register("table", {
|
|
|
9313
8819
|
};
|
|
9314
8820
|
const layoutInCell = (inner, x, cw, st, cctx, row) => {
|
|
9315
8821
|
const pd = padding(st);
|
|
9316
|
-
const it = { ...inner, name: inner.name || `${item.name}_cell`, x: x + pd[3], y: 0, w: Math.max(1, cw - pd[1] - pd[3]), h: Number(inner.h) || Math.max(1, (row.height || 18) - pd[0] - pd[2]) };
|
|
8822
|
+
const it = { ...inner, name: inner.name || `${item.name || item.type}_cell`, x: x + pd[3], y: 0, w: Math.max(1, cw - pd[1] - pd[3]), h: Number(inner.h) || Math.max(1, (row.height || 18) - pd[0] - pd[2]) };
|
|
9317
8823
|
const scopeRows2 = cctx.aggIndex != null ? [cctx.fields] : cctx.aggRows || [];
|
|
9318
8824
|
const current = item.dataSet ? { ...cctx.current || {}, [item.dataSet]: scopeRows2 } : cctx.current;
|
|
9319
8825
|
const child = {
|
|
@@ -9333,7 +8839,7 @@ register("table", {
|
|
|
9333
8839
|
const reads = (it.dataSet && it.dataSet !== item.dataSet ? child.ctxFor(it).aggRows?.length : scopeRows2.length) || 1;
|
|
9334
8840
|
if (budget.left < reads) {
|
|
9335
8841
|
budget.left = 0;
|
|
9336
|
-
lc.warn(`${item.name}: more than ${CELL_ITEM_UNITS} rows of items inside cells; the rest are left empty`);
|
|
8842
|
+
lc.warn(`${item.name || item.type}: more than ${CELL_ITEM_UNITS} rows of items inside cells; the rest are left empty`);
|
|
9337
8843
|
return { items: [], height: 0 };
|
|
9338
8844
|
}
|
|
9339
8845
|
budget.left -= reads;
|
|
@@ -9367,8 +8873,8 @@ register("table", {
|
|
|
9367
8873
|
const st = k === 0 && n === 1 ? s.st : { ...s.st, ...k > 0 ? { borderTop: "none" } : {}, ...k < n - 1 ? { borderBottom: "none" } : {} };
|
|
9368
8874
|
const add2 = paintBox({ x: s.x, y: 0, w: s.w, h: u.height, st, tl: null, m: lc.m });
|
|
9369
8875
|
if (k === 0) {
|
|
9370
|
-
const
|
|
9371
|
-
const text = paintBox({ x: s.x, y: 0, w: s.w, h: H, st: { ...s.st, ...NO_BOX }, tl: s.tl, numeric: s.numeric, m: lc.m, cell:
|
|
8876
|
+
const tag3 = lc.regions ? { table: item.name, row: u.rowId, col: colX.indexOf(s.x), span: s.cols, rowSpan: n, kind, cols: colWidths, bg: s.st.backgroundColor || null } : null;
|
|
8877
|
+
const text = paintBox({ x: s.x, y: 0, w: s.w, h: H, st: { ...s.st, ...NO_BOX }, tl: s.tl, numeric: s.numeric, m: lc.m, cell: tag3 });
|
|
9372
8878
|
add2.splice(s.st.backgroundColor ? 1 : 0, 0, ...text);
|
|
9373
8879
|
cellLinks(add2, s, u.height, kind, s.level);
|
|
9374
8880
|
if (s.toggle) add2.push(toggleLink(s, s.w, u.height));
|
|
@@ -9377,7 +8883,7 @@ register("table", {
|
|
|
9377
8883
|
u.items.splice(at < 0 ? u.items.length : at, 0, ...add2);
|
|
9378
8884
|
});
|
|
9379
8885
|
if (s.tl.needed > s.units[0].height + GROW_TOLERANCE || s.st.verticalAlign && s.st.verticalAlign !== "top") {
|
|
9380
|
-
const id = `${item.name}:span:${lc.nextId()}`;
|
|
8886
|
+
const id = `${item.name || item.type}:span:${lc.nextId()}`;
|
|
9381
8887
|
for (const u of s.units) u.block || (u.block = id);
|
|
9382
8888
|
} else {
|
|
9383
8889
|
let above = s.units[0].height;
|
|
@@ -9419,7 +8925,7 @@ register("table", {
|
|
|
9419
8925
|
const groups = item.groups || [];
|
|
9420
8926
|
let rowNo = lc.win?.rowOffset || 0;
|
|
9421
8927
|
const groupTables = [];
|
|
9422
|
-
const tableKey = `${item.name}#${lc.nextId()}`;
|
|
8928
|
+
const tableKey = `${item.name || item.type}#${lc.nextId()}`;
|
|
9423
8929
|
const emit = (level, grows, ctx, chain, keyPath) => {
|
|
9424
8930
|
if (level === groups.length) {
|
|
9425
8931
|
for (let i = 0; i < grows.length; i++) {
|
|
@@ -9481,7 +8987,7 @@ register("table", {
|
|
|
9481
8987
|
kids = /* @__PURE__ */ new Map();
|
|
9482
8988
|
roots = [];
|
|
9483
8989
|
for (const b of buckets) {
|
|
9484
|
-
const pk = evalSafe(g.parent, { ...ctx, fields: b.rows[0], aggRows: b.rows, aggIndex: void 0 }, lc, `${item.name} group parent`).v;
|
|
8990
|
+
const pk = evalSafe(g.parent, { ...ctx, fields: b.rows[0], aggRows: b.rows, aggIndex: void 0 }, lc, `${item.name || item.type} group parent`).v;
|
|
9485
8991
|
const pr = pk == null || pk === "" ? null : byText.get(pk instanceof Date ? String(pk.getTime()) : String(pk));
|
|
9486
8992
|
if (!pr || pr === b) roots.push(b);
|
|
9487
8993
|
else {
|
|
@@ -9513,7 +9019,7 @@ register("table", {
|
|
|
9513
9019
|
if (g.indent) gctx.indent = (ctx.indent || 0) + depth * Number(g.indent);
|
|
9514
9020
|
}
|
|
9515
9021
|
const path = `${parentPath}/${bk.text}`;
|
|
9516
|
-
const tkey = `${item.name}|${level}|${path}`;
|
|
9022
|
+
const tkey = `${item.name || item.type}|${level}|${path}`;
|
|
9517
9023
|
const dflt = kids && g.expandLevels != null ? depth < Number(g.expandLevels) : !g.initiallyCollapsed;
|
|
9518
9024
|
const open = !g.collapsible ? true : lc.state?.toggles?.[tkey] ?? dflt;
|
|
9519
9025
|
const start = units.length;
|
|
@@ -9535,7 +9041,7 @@ register("table", {
|
|
|
9535
9041
|
const ch = kids?.get(bk) || [];
|
|
9536
9042
|
if (open) {
|
|
9537
9043
|
emit(level + 1, bk.rows, gctx, [...chain, sc], path);
|
|
9538
|
-
if (ch.length && depth + 1 >= MAX_TREE_DEPTH) lc.warn(`${item.name}: the group "${gname}" is more than ${MAX_TREE_DEPTH} levels deep; deeper rows are listed at the top level`);
|
|
9044
|
+
if (ch.length && depth + 1 >= MAX_TREE_DEPTH) lc.warn(`${item.name || item.type}: the group "${gname}" is more than ${MAX_TREE_DEPTH} levels deep; deeper rows are listed at the top level`);
|
|
9539
9045
|
else ch.forEach((c, ci) => node(c, ci, ch, depth + 1, path));
|
|
9540
9046
|
} else {
|
|
9541
9047
|
const rs = recOf.get(bk) || bk.rows;
|
|
@@ -9564,7 +9070,7 @@ register("table", {
|
|
|
9564
9070
|
if (g.newSection) units[start].sectionStart = true;
|
|
9565
9071
|
if (g.pageBreak === "after" && bi < sibs.length - 1) units[units.length - 1].breakAfter = true;
|
|
9566
9072
|
if (g.keepTogether) {
|
|
9567
|
-
const id = `${item.name}:g${level}:${path}:${lc.nextId()}`;
|
|
9073
|
+
const id = `${item.name || item.type}:g${level}:${path}:${lc.nextId()}`;
|
|
9568
9074
|
for (let k = start; k < units.length; k++) if (!units[k].block) {
|
|
9569
9075
|
units[k].block = id;
|
|
9570
9076
|
if (cont) units[k].blockOpen = true;
|
|
@@ -9585,7 +9091,7 @@ register("table", {
|
|
|
9585
9091
|
if (kids) {
|
|
9586
9092
|
const rest = buckets.filter((b) => !shown.has(b));
|
|
9587
9093
|
if (rest.length) {
|
|
9588
|
-
lc.warn(`${item.name}: ${rest.length} rows of the group "${gname}" have no root (a cycle or too deep); they are listed at the top level`);
|
|
9094
|
+
lc.warn(`${item.name || item.type}: ${rest.length} rows of the group "${gname}" have no root (a cycle or too deep); they are listed at the top level`);
|
|
9589
9095
|
rest.forEach((bk, bi) => node(bk, bi, rest, 0, keyPath));
|
|
9590
9096
|
}
|
|
9591
9097
|
}
|
|
@@ -9613,7 +9119,7 @@ register("table", {
|
|
|
9613
9119
|
units[units.length - 1].breakAfter = true;
|
|
9614
9120
|
}
|
|
9615
9121
|
if (item.bookmark && units.length) {
|
|
9616
|
-
const b = evalSafe(item.bookmark, base, lc, `${item.name ||
|
|
9122
|
+
const b = evalSafe(item.bookmark, base, lc, `${item.name || item.type} bookmark`).v;
|
|
9617
9123
|
if (b != null && b !== "" && b !== "#Error") units[0].items.unshift({ t: "bookmark", label: String(b), x: item.x, y: 0, level: item.bookmarkLevel ?? 0 });
|
|
9618
9124
|
}
|
|
9619
9125
|
if (!lc.fixed) lc.regions?.push(region);
|
|
@@ -9708,7 +9214,7 @@ register("container", {
|
|
|
9708
9214
|
const ctx = lc.ctxFor(item);
|
|
9709
9215
|
if (hidden(item, ctx, lc)) return { height: 0, units: [] };
|
|
9710
9216
|
const res = lc.layoutItems(item.items, lc, { dx: item.x });
|
|
9711
|
-
return frame2(item, lc, ctx, res, item.keepTogether !== false ? `c:${item.name}:${lc.nextId()}` : null);
|
|
9217
|
+
return frame2(item, lc, ctx, res, item.keepTogether !== false ? `c:${item.name || item.type}:${lc.nextId()}` : null);
|
|
9712
9218
|
}
|
|
9713
9219
|
});
|
|
9714
9220
|
function frame2(item, lc, ctx, res, blockId0) {
|
|
@@ -9765,7 +9271,7 @@ register("list", {
|
|
|
9765
9271
|
let rows = item.dataSet && lc.dataSets[item.dataSet] || [];
|
|
9766
9272
|
if (item.inheritRows && item.dataSet && ctx.current?.[item.dataSet]) rows = ctx.current[item.dataSet];
|
|
9767
9273
|
else rows = related(rows, ctx);
|
|
9768
|
-
if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name}: the data set "${item.dataSet}" does not exist`);
|
|
9274
|
+
if (item.dataSet && !lc.dataSets[item.dataSet]) lc.warn(`${item.name || item.type}: the data set "${item.dataSet}" does not exist`);
|
|
9769
9275
|
if (item.filters?.length) rows = applyFilters(rows, item.filters, ctx);
|
|
9770
9276
|
if (item.sort?.length) rows = applySort(rows, item.sort, ctx);
|
|
9771
9277
|
const reps = item.groupBy ? groupRows(rows, item.groupBy, ctx, lc, item.name, item.groupAdjacent === true).map((b) => b.rows) : rows.map((r) => [r]);
|
|
@@ -9794,7 +9300,7 @@ register("list", {
|
|
|
9794
9300
|
} };
|
|
9795
9301
|
const k = i % ncol;
|
|
9796
9302
|
const res = lc.layoutItems(item.items, child, { dx: item.x + k * (item.w + gap) });
|
|
9797
|
-
const fr = frame2({ ...item, x: item.x + k * (item.w + gap), name: `${item.name}[${i + 1}]` }, child, rctx, res, item.keepTogether !== false ? `l:${item.name}:${i}:${lc.nextId()}` : null);
|
|
9303
|
+
const fr = frame2({ ...item, x: item.x + k * (item.w + gap), name: `${item.name || item.type}[${i + 1}]` }, child, rctx, res, item.keepTogether !== false ? `l:${item.name || item.type}:${i}:${lc.nextId()}` : null);
|
|
9798
9304
|
if (ncol > 1) {
|
|
9799
9305
|
if (k === 0) {
|
|
9800
9306
|
cells = { top: y, height: 0, items: [], x0: item.x, x1: item.x + ncol * item.w + (ncol - 1) * gap, block: void 0 };
|
|
@@ -10265,7 +9771,7 @@ register("subreport", {
|
|
|
10265
9771
|
if (hidden(item, ctx, lc)) return { height: 0, units: [] };
|
|
10266
9772
|
const params = {};
|
|
10267
9773
|
for (const [k, v] of Object.entries(item.params || {})) {
|
|
10268
|
-
const r = evalSafe(v, ctx, lc, `${item.name} parameter ${k}`);
|
|
9774
|
+
const r = evalSafe(v, ctx, lc, `${item.name || item.type} parameter ${k}`);
|
|
10269
9775
|
params[k] = r.v instanceof Date ? isoDate(r.v, ctx.timeZone) : r.v;
|
|
10270
9776
|
}
|
|
10271
9777
|
const key = `${item.report}|${JSON.stringify(params)}`;
|
|
@@ -10284,10 +9790,10 @@ register("subreport", {
|
|
|
10284
9790
|
const res = sub.results.get(key);
|
|
10285
9791
|
if (!res) return placeholder(`Subreport "${item.report}" did not load`);
|
|
10286
9792
|
if (res.error) {
|
|
10287
|
-
lc.warn(`${item.name}: ${res.error}`);
|
|
9793
|
+
lc.warn(`${item.name || item.type}: ${res.error}`);
|
|
10288
9794
|
return placeholder(`Subreport "${item.report}": ${res.error}`);
|
|
10289
9795
|
}
|
|
10290
|
-
const block = item.keepTogether ? `s:${item.name}:${lc.nextId()}` : void 0;
|
|
9796
|
+
const block = item.keepTogether ? `s:${item.name || item.type}:${lc.nextId()}` : void 0;
|
|
10291
9797
|
const units = res.units.map((u) => ({ ...u, items: shiftItems(u.items, item.x, 0), ...u.cont ? { cont: shiftItems(u.cont, item.x, 0) } : {}, x0: (u.x0 ?? 0) + item.x, x1: (u.x1 ?? item.w) + item.x, block: u.block || block, tableId: void 0 }));
|
|
10292
9798
|
const tables = res.tables.map((t) => ({ ...t, x0: t.x0 + item.x, x1: t.x1 + item.x, headerItems: shiftItems(t.headerItems, item.x, 0), id: `sub${lc.nextId()}` }));
|
|
10293
9799
|
for (const r of res.regions || []) lc.regions?.push(r);
|
|
@@ -10780,7 +10286,7 @@ function layoutItems(items, lc, { dx = 0 } = {}) {
|
|
|
10780
10286
|
const own = L.res.table ? { id: lc.nextId(), ...L.res.table } : null;
|
|
10781
10287
|
const us = L.res.units;
|
|
10782
10288
|
const alt = it.alt || L.res.alt;
|
|
10783
|
-
const
|
|
10289
|
+
const tag3 = alt || it.headingLevel || FIGURES.has(it.type) ? { k: it.type, id: lc.nextId(), alt: alt ? String(alt).slice(0, 2e3) : void 0, h: Number(it.headingLevel) || void 0 } : null;
|
|
10784
10290
|
for (let k = 0; k < us.length; k++) {
|
|
10785
10291
|
const u = us[k];
|
|
10786
10292
|
u.x0 = u.x0 ?? x0;
|
|
@@ -10789,7 +10295,7 @@ function layoutItems(items, lc, { dx = 0 } = {}) {
|
|
|
10789
10295
|
u.tableId = own ? own.id : u.tableId;
|
|
10790
10296
|
u.breakBefore = u.breakBefore || k === 0 && !!it.pageBreakBefore;
|
|
10791
10297
|
u.breakAfter = u.breakAfter || k === us.length - 1 && !!it.pageBreakAfter;
|
|
10792
|
-
if (
|
|
10298
|
+
if (tag3) for (const d of u.items) d.tag ?? (d.tag = tag3);
|
|
10793
10299
|
units.push(u);
|
|
10794
10300
|
}
|
|
10795
10301
|
if (own) tables.push({ ...own, top, bottom: top + L.res.height, x0, x1 });
|
|
@@ -13338,6 +12844,7 @@ var SCHEMA_ALIAS = "reportwright/report@1";
|
|
|
13338
12844
|
var ITEM_KEYS = new Set("id type name x y w h style styleName items hidden layer locked label alt value dataSet canGrow canShrink action bookmark tooltip keepTogether pageBreakBefore pageBreakAfter pageBreakBetween newSection filters sort groups groupBy groupAdjacent inheritRows columns header detail footer repeatHeader widowControl noRowsText pageBreakEvery cells rows rowGroup rowGroups columnGro\
|
|
13339
12845
|
up columnGroups values value valueFormat rowSort columnSort rowTotals columnTotals cellStyle headerStyle totalStyle corner partProps library part properties overflow overflowTo src fit headingLevel rotate shrinkToFit minFontSize shrinkStep tocDots tocLink tocPage format chartType series seriesGroup category title legend palette stacked valueAxis xTitle maxCategories outliers categorySort categoryF\
|
|
13340
12846
|
ormat showValues labelPosition symbology quietZone report params text toggle initiallyCollapsed collapsible expandLevels repeatColumns columnGap rowHeaderWidth anim animation visual comment notes description".split(" "));
|
|
12847
|
+
var NO_SERIES_CHARTS = /* @__PURE__ */ new Set(["gantt"]);
|
|
13341
12848
|
function looksLikeLink(it) {
|
|
13342
12849
|
if (it.type !== "textbox" || it.action && it.action.type && it.action.type !== "none") return false;
|
|
13343
12850
|
const st = it.style && typeof it.style === "object" ? it.style : {};
|
|
@@ -13466,6 +12973,9 @@ function validate(def) {
|
|
|
13466
12973
|
if (p?.hidden && p.required && (p.default == null || p.default === "") && !p.nullable) {
|
|
13467
12974
|
warnings.push(`Parameter "${p.name}" is hidden and required but has no default: only a URL, the API or a drill-through can set it`);
|
|
13468
12975
|
}
|
|
12976
|
+
if (p?.required && p.nullable && !p.multi && (p.default == null || p.default === "")) {
|
|
12977
|
+
warnings.push(`Parameter "${p.name}" is required and nullable with no default: when no value is given it is null and the report can draw blank; give it a default, or drop "nullable" to require a value`);
|
|
12978
|
+
}
|
|
13469
12979
|
if (p?.type === "date" && typeof p.default === "string" && p.default.trim() && !p.default.startsWith("=") && !toDate(p.default)) {
|
|
13470
12980
|
warnings.push(`Parameter "${p.name}": its default "${p.default}" is not a date; write it as 2026-12-31 (year-month-day)`);
|
|
13471
12981
|
}
|
|
@@ -13546,7 +13056,7 @@ function validate(def) {
|
|
|
13546
13056
|
warnings.push(`${label}: there is no named style "${it.styleName}"${g ? ` (did you mean "${g}"?)` : ""}; it is ignored`);
|
|
13547
13057
|
}
|
|
13548
13058
|
if (Array.isArray(it.sort)) sortIssues(it.sort, label, warnings);
|
|
13549
|
-
if (it.type === "chart" && Array.isArray(it.series) &&
|
|
13059
|
+
if (it.type === "chart" && !NO_SERIES_CHARTS.has(it.chartType) && !(Array.isArray(it.series) && it.series.length)) warnings.push(`${label}: the chart has no series, so it plots a count of its rows; add one, e.g. series: [{ "name": "Value", "value": "=Sum(Fields.Amount)" }]`);
|
|
13550
13060
|
if (it.type === "image" && typeof it.src === "string" && !it.src.startsWith("=") && LOCAL_IMAGE.test(it.src.trim())) warnings.push(`${label}: "${it.src}" is a local file path, which a browser cannot load; embed the image (images: { name: "data:image/png;base64,..." } and src: "embedded:name") or use an https:// address`);
|
|
13551
13061
|
if (it.style != null && (typeof it.style !== "object" || Array.isArray(it.style))) warnings.push(`${label}: "style" must be an object of style properties, such as { "fontWeight": "bold" }; ${JSON.stringify(it.style).slice(0, 40)} is ignored`);
|
|
13552
13062
|
const own = ownKeys(it.type);
|
|
@@ -13589,6 +13099,9 @@ function validate(def) {
|
|
|
13589
13099
|
const n = Array.isArray(it.columns) ? it.columns.length : 0;
|
|
13590
13100
|
const rowsOf = [["header", it.header], ["detail", it.detail], ["footer", it.footer], ...(it.groups || []).flatMap((g, gi) => [[`group ${gi + 1} header`, g?.header], [`group ${gi + 1} footer`, g?.footer]])];
|
|
13591
13101
|
for (const [part, rows] of rowsOf) (Array.isArray(rows) ? rows : []).forEach((row, ri) => {
|
|
13102
|
+
const cells = Array.isArray(row?.cells) ? row.cells : [];
|
|
13103
|
+
const placed = cells.length < n && cells.some((c) => Number(c?.colSpan) > 1) ? cells.reduce((s, c) => s + Math.max(1, Math.floor(Number(c?.colSpan)) || 1), 0) : cells.length;
|
|
13104
|
+
if (placed < n) warnings.push(`${label} ${part} row ${ri + 1}: the row has ${placed} of ${n} cells and no colSpan covers the other ${n - placed}; add the cells or a colSpan`);
|
|
13592
13105
|
for (const [k, j] of rowCells(row?.cells, n).covered) warnings.push(`${label} ${part} row ${ri + 1}: cell ${k} is covered by colSpan of cell ${j}, so it is not drawn (leave it null, or omit the spanned cells: a row with fewer cells than columns places each cell after the previous one's span)`);
|
|
13593
13106
|
});
|
|
13594
13107
|
}
|
|
@@ -13687,7 +13200,7 @@ function* textsOf(def, dataSets) {
|
|
|
13687
13200
|
}
|
|
13688
13201
|
}
|
|
13689
13202
|
}
|
|
13690
|
-
var NO_CJK_FONTS = "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm run fonts:cjk";
|
|
13203
|
+
var NO_CJK_FONTS = "Text in Japanese/Chinese/Korean needs the optional CJK fonts: npm i @reportwright/fonts-cjk, then npx reportwright-fonts-cjk (npm); in the app, npm run fonts:cjk";
|
|
13691
13204
|
async function render(def0, opts = {}) {
|
|
13692
13205
|
const t0 = Date.now();
|
|
13693
13206
|
const reuseWarnings = [];
|
|
@@ -13758,6 +13271,7 @@ async function render(def0, opts = {}) {
|
|
|
13758
13271
|
const base = { ...BASE_STYLE, ...def.styles?.base || {} };
|
|
13759
13272
|
let idSeq = 0;
|
|
13760
13273
|
const regions = [];
|
|
13274
|
+
const tablesSeen = { n: 0 };
|
|
13761
13275
|
const imageBudget = opts.imageBudget || { seen: /* @__PURE__ */ new Set(), bytes: 0 };
|
|
13762
13276
|
const makeLc = (globals, fixed) => {
|
|
13763
13277
|
const lc = {
|
|
@@ -13768,6 +13282,7 @@ async function render(def0, opts = {}) {
|
|
|
13768
13282
|
fixed,
|
|
13769
13283
|
maxUnitHeight: contentH,
|
|
13770
13284
|
state: opts.state || {},
|
|
13285
|
+
tablesSeen,
|
|
13771
13286
|
regions: opts.exportData || opts.dataOnly ? regions : null,
|
|
13772
13287
|
dataOnly: !!opts.dataOnly,
|
|
13773
13288
|
// exportData: true: table data and cell tags (Excel, CSV, Word, HTML, accessible PDF)
|
|
@@ -14070,9 +13585,9 @@ async function render(def0, opts = {}) {
|
|
|
14070
13585
|
for (const k of store.failed) if (k !== SHAPER && !OPTIONAL_KEYS.includes(k)) warn(`The font "${k}" did not load, so its text is drawn in a bundled face instead.`);
|
|
14071
13586
|
if (m.unshaped) warn("The text shaper (harfbuzz.wasm) did not load, so Arabic, Hebrew, Indic and Thai text is not shaped (letters unjoined, marks misplaced). A font loader must map each key with fontFile(key).");
|
|
14072
13587
|
if (m.noGlyph.size) {
|
|
14073
|
-
const
|
|
13588
|
+
const cjk = [...m.noGlyph].some((c) => /[\u1100-\u11ff\u2600-\u27bf\u2b00-\u2bff\u3000-\u9fff\uac00-\ud7af\uf900-\ufaff\uff00-\uffef]|[\u{1f000}-\u{1faff}]/u.test(c));
|
|
14074
13589
|
const list = [...m.noGlyph].slice(0, 20).join(" ");
|
|
14075
|
-
warn(
|
|
13590
|
+
warn(cjk ? `${NO_CJK_FONTS}. Until then these characters show as boxes: ${list}.` : `No font has these characters, so they show as boxes: ${list}. Upload a font that has them and use it for this text.`);
|
|
14076
13591
|
}
|
|
14077
13592
|
if (opts.bodyOnly) return { units: result.body.units, tables: result.body.tables, height: result.body.height, regions, warnings, clipped: clipped() };
|
|
14078
13593
|
if (opts.dataOnly) return {
|
|
@@ -14112,6 +13627,7 @@ async function render(def0, opts = {}) {
|
|
|
14112
13627
|
headings: result.headings || [],
|
|
14113
13628
|
pageStarts: result.pageStarts || [],
|
|
14114
13629
|
regions: opts.exportData ? regions : null,
|
|
13630
|
+
hasTables: tablesSeen.n > 0,
|
|
14115
13631
|
name: def.name || "",
|
|
14116
13632
|
partial: cutRows(),
|
|
14117
13633
|
timeZone: timeZone || null,
|
|
@@ -14248,7 +13764,8 @@ async function parameterOptions(def, given = {}, opts = {}) {
|
|
|
14248
13764
|
seen.add(k);
|
|
14249
13765
|
list.push({ value: v instanceof Date ? isoDate(v) : v, label: String(p.optionsFrom.label ? evalValue(p.optionsFrom.label, c) : v) });
|
|
14250
13766
|
}
|
|
14251
|
-
out[p.name] =
|
|
13767
|
+
const fixed = out[p.name] || [], have = new Set(fixed.map((o) => o.value));
|
|
13768
|
+
out[p.name] = [...fixed, ...list.filter((o) => !have.has(o.value))];
|
|
14252
13769
|
}
|
|
14253
13770
|
return out;
|
|
14254
13771
|
}
|
|
@@ -14296,440 +13813,16 @@ function resolveParameters(def, given, o = {}) {
|
|
|
14296
13813
|
return out;
|
|
14297
13814
|
}
|
|
14298
13815
|
|
|
14299
|
-
// src/exporters/subset.js
|
|
14300
|
-
var HB_MEMORY_MODE_WRITABLE = 2;
|
|
14301
|
-
var HB_SUBSET_SETS_DROP_TABLE_TAG = 3;
|
|
14302
|
-
var HB_SUBSET_SETS_LAYOUT_FEATURE_TAG = 6;
|
|
14303
|
-
var HB_SUBSET_FLAGS_RETAIN_GIDS = 2;
|
|
14304
|
-
var HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 64;
|
|
14305
|
-
var tag3 = (s) => [...s].reduce((a, c) => (a << 8) + c.charCodeAt(0), 0) >>> 0;
|
|
14306
|
-
function createSubsetter(loadWasm) {
|
|
14307
|
-
let hbP = null;
|
|
14308
|
-
const hb = () => hbP || (hbP = (async () => {
|
|
14309
|
-
const bytes = await loadWasm();
|
|
14310
|
-
const { instance } = await WebAssembly.instantiate(bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes), {});
|
|
14311
|
-
const x = (
|
|
14312
|
-
/** @type {any} */
|
|
14313
|
-
instance.exports
|
|
14314
|
-
);
|
|
14315
|
-
x._initialize?.();
|
|
14316
|
-
return x;
|
|
14317
|
-
})());
|
|
14318
|
-
return async function subset(font, codePoints, opt = {}) {
|
|
14319
|
-
const x = await hb();
|
|
14320
|
-
const heap = () => new Uint8Array(x.memory.buffer);
|
|
14321
|
-
const input = x.hb_subset_input_create_or_fail();
|
|
14322
|
-
if (!input) throw new Error("HarfBuzz: no subset input");
|
|
14323
|
-
const buf = x.malloc(font.byteLength);
|
|
14324
|
-
heap().set(font, buf);
|
|
14325
|
-
const blob = x.hb_blob_create(buf, font.byteLength, HB_MEMORY_MODE_WRITABLE, 0, 0);
|
|
14326
|
-
const face = x.hb_face_create(blob, 0);
|
|
14327
|
-
x.hb_blob_destroy(blob);
|
|
14328
|
-
try {
|
|
14329
|
-
if (!opt.layout) {
|
|
14330
|
-
x.hb_set_clear(x.hb_subset_input_set(input, HB_SUBSET_SETS_LAYOUT_FEATURE_TAG));
|
|
14331
|
-
const drop = x.hb_subset_input_set(input, HB_SUBSET_SETS_DROP_TABLE_TAG);
|
|
14332
|
-
for (const t of ["GSUB", "GPOS", "GDEF", "kern", "morx", "mort"]) x.hb_set_add(drop, tag3(t));
|
|
14333
|
-
}
|
|
14334
|
-
x.hb_subset_input_set_flags(input, x.hb_subset_input_get_flags(input) | HB_SUBSET_FLAGS_NOTDEF_OUTLINE);
|
|
14335
|
-
const uni = x.hb_subset_input_unicode_set(input);
|
|
14336
|
-
for (const cp of codePoints) x.hb_set_add(uni, cp);
|
|
14337
|
-
if (opt.glyphs) {
|
|
14338
|
-
x.hb_subset_input_set_flags(input, x.hb_subset_input_get_flags(input) | HB_SUBSET_FLAGS_RETAIN_GIDS);
|
|
14339
|
-
const gs = x.hb_subset_input_glyph_set(input);
|
|
14340
|
-
for (const g of opt.glyphs) x.hb_set_add(gs, g);
|
|
14341
|
-
}
|
|
14342
|
-
x.hb_subset_input_pin_all_axes_to_default(input, face);
|
|
14343
|
-
const sub = x.hb_subset_or_fail(face, input);
|
|
14344
|
-
if (!sub) throw new Error("HarfBuzz: subsetting failed");
|
|
14345
|
-
const res = x.hb_face_reference_blob(sub);
|
|
14346
|
-
const off = x.hb_blob_get_data(res, 0);
|
|
14347
|
-
const len = x.hb_blob_get_length(res);
|
|
14348
|
-
const out = heap().slice(off, off + len);
|
|
14349
|
-
x.hb_blob_destroy(res);
|
|
14350
|
-
x.hb_face_destroy(sub);
|
|
14351
|
-
if (!len) throw new Error("HarfBuzz: empty subset");
|
|
14352
|
-
return out;
|
|
14353
|
-
} finally {
|
|
14354
|
-
x.hb_subset_input_destroy(input);
|
|
14355
|
-
x.hb_face_destroy(face);
|
|
14356
|
-
x.free(buf);
|
|
14357
|
-
}
|
|
14358
|
-
};
|
|
14359
|
-
}
|
|
14360
|
-
|
|
14361
|
-
// src/viewer/engineClient.js
|
|
14362
|
-
var assetBase = "";
|
|
14363
|
-
function setAssetBase(url) {
|
|
14364
|
-
assetBase = String(url || "").replace(/\/$/, "");
|
|
14365
|
-
mainStore = null;
|
|
14366
|
-
}
|
|
14367
|
-
var origin = () => assetBase || window.location.origin;
|
|
14368
|
-
var fontsUrl = null;
|
|
14369
|
-
var standalone = false;
|
|
14370
|
-
function setStandalone({ standalone: on = false, fontsUrl: f4 = null } = {}) {
|
|
14371
|
-
standalone = !!on;
|
|
14372
|
-
fontsUrl = f4 ? String(f4).replace(/\/?$/, "/") : null;
|
|
14373
|
-
mainStore = null;
|
|
14374
|
-
fontList = null;
|
|
14375
|
-
fontBytes.clear();
|
|
14376
|
-
}
|
|
14377
|
-
var fontsBase = () => fontsUrl || `${assetBase}/fonts/`;
|
|
14378
|
-
var besideFonts = (file) => fontsUrl ? new URL(`../${file}`, new URL(fontsUrl, typeof location !== "undefined" ? location.href : void 0)).href : `${assetBase}/${file}`;
|
|
14379
|
-
var subsetter = createSubsetter(async () => (await fetch(besideFonts("harfbuzz-subset.wasm"))).arrayBuffer());
|
|
14380
|
-
var worker = null;
|
|
14381
|
-
var workerBroken = false;
|
|
14382
|
-
var seq = 0;
|
|
14383
|
-
var waiting = /* @__PURE__ */ new Map();
|
|
14384
|
-
var mainStore = null;
|
|
14385
|
-
var bundledFonts = null;
|
|
14386
|
-
var bundledList = null;
|
|
14387
|
-
function setBundledFonts(map, list) {
|
|
14388
|
-
bundledFonts = map || null;
|
|
14389
|
-
bundledList = list || null;
|
|
14390
|
-
mainStore = null;
|
|
14391
|
-
fontList = null;
|
|
14392
|
-
}
|
|
14393
|
-
var b64ToBuffer = (b64) => {
|
|
14394
|
-
const s = atob(b64);
|
|
14395
|
-
const u = new Uint8Array(s.length);
|
|
14396
|
-
for (let i = 0; i < s.length; i++) u[i] = s.charCodeAt(i);
|
|
14397
|
-
return u.buffer;
|
|
14398
|
-
};
|
|
14399
|
-
var fontBytes = /* @__PURE__ */ new Map();
|
|
14400
|
-
var fetchFont = (k) => {
|
|
14401
|
-
if (!fontBytes.has(k)) {
|
|
14402
|
-
const p = (async () => {
|
|
14403
|
-
const r = await fetch(`${fontsBase()}${fontFile(k)}`);
|
|
14404
|
-
if (!r.ok) throw new Error(`Font "${k}" did not load (HTTP ${r.status})`);
|
|
14405
|
-
return r.arrayBuffer();
|
|
14406
|
-
})();
|
|
14407
|
-
p.catch(() => fontBytes.delete(k));
|
|
14408
|
-
fontBytes.set(k, p);
|
|
14409
|
-
}
|
|
14410
|
-
return fontBytes.get(k);
|
|
14411
|
-
};
|
|
14412
|
-
var getFont = async (k) => {
|
|
14413
|
-
if (bundledFonts?.[k]) return b64ToBuffer(bundledFonts[k]);
|
|
14414
|
-
return (await fetchFont(k)).slice(0);
|
|
14415
|
-
};
|
|
14416
|
-
function fontStore() {
|
|
14417
|
-
if (!mainStore) mainStore = new FontStore(getFont);
|
|
14418
|
-
return mainStore;
|
|
14419
|
-
}
|
|
14420
|
-
var makeWorker = true ? null : () => new Worker(new URL("../worker/engine.worker.js", import.meta.url), { type: "module" });
|
|
14421
|
-
var fontList = null;
|
|
14422
|
-
function customFonts(refresh = false) {
|
|
14423
|
-
if (!fontList || refresh) {
|
|
14424
|
-
fontList = (bundledList ? Promise.resolve(bundledList) : standalone ? Promise.resolve([]) : fetch(`${assetBase}/api/fonts`).then((r) => r.ok ? r.json() : []).catch(() => [])).then((list) => {
|
|
14425
|
-
setCustomFonts(list);
|
|
14426
|
-
return list;
|
|
14427
|
-
});
|
|
14428
|
-
}
|
|
14429
|
-
return fontList;
|
|
14430
|
-
}
|
|
14431
|
-
var faces = /* @__PURE__ */ new Map();
|
|
14432
|
-
function loadReportFonts(keys) {
|
|
14433
|
-
if (typeof document === "undefined" || !document.fonts || typeof FontFace === "undefined") return Promise.resolve([]);
|
|
14434
|
-
return Promise.all(keys.filter((k) => k !== SHAPER).map((k) => {
|
|
14435
|
-
if (!faces.has(k)) {
|
|
14436
|
-
const bytes = bundledFonts?.[k] ? Promise.resolve(b64ToBuffer(bundledFonts[k])) : fetchFont(k).then((b) => b.slice(0));
|
|
14437
|
-
faces.set(k, bytes.then((src) => {
|
|
14438
|
-
const face = new FontFace(fontFamilyCss(k), src, { display: "block" });
|
|
14439
|
-
document.fonts.add(face);
|
|
14440
|
-
return face.load();
|
|
14441
|
-
}).then(() => null, (error) => {
|
|
14442
|
-
faces.delete(k);
|
|
14443
|
-
return { key: k, error };
|
|
14444
|
-
}));
|
|
14445
|
-
}
|
|
14446
|
-
return faces.get(k);
|
|
14447
|
-
})).then((results) => results.filter(Boolean));
|
|
14448
|
-
}
|
|
14449
|
-
var loadModelFonts = (model) => loadReportFonts([.../* @__PURE__ */ new Set([...ALL_FONT_KEYS, ...fontKeysOf(model)])]);
|
|
14450
|
-
function getWorker() {
|
|
14451
|
-
if (!makeWorker || workerBroken || typeof Worker === "undefined" || assetBase || bundledFonts) return null;
|
|
14452
|
-
if (worker) return worker;
|
|
14453
|
-
try {
|
|
14454
|
-
worker = makeWorker();
|
|
14455
|
-
worker.onmessage = (e) => {
|
|
14456
|
-
const w = waiting.get(e.data.id);
|
|
14457
|
-
if (!w) return;
|
|
14458
|
-
if (e.data.partial) {
|
|
14459
|
-
w.onPartial?.(e.data.model);
|
|
14460
|
-
return;
|
|
14461
|
-
}
|
|
14462
|
-
if (e.data.retry) {
|
|
14463
|
-
w.onRetry?.(e.data.retry);
|
|
14464
|
-
return;
|
|
14465
|
-
}
|
|
14466
|
-
waiting.delete(e.data.id);
|
|
14467
|
-
if (e.data.ok) w.resolve(e.data.model);
|
|
14468
|
-
else w.reject(Object.assign(new Error(e.data.error.message), e.data.error));
|
|
14469
|
-
};
|
|
14470
|
-
worker.onerror = () => {
|
|
14471
|
-
workerBroken = true;
|
|
14472
|
-
worker = null;
|
|
14473
|
-
for (const [, w] of waiting) w.retry();
|
|
14474
|
-
waiting.clear();
|
|
14475
|
-
};
|
|
14476
|
-
} catch {
|
|
14477
|
-
workerBroken = true;
|
|
14478
|
-
worker = null;
|
|
14479
|
-
}
|
|
14480
|
-
return worker;
|
|
14481
|
-
}
|
|
14482
|
-
var bundled = /* @__PURE__ */ new Map();
|
|
14483
|
-
function registerReports(map) {
|
|
14484
|
-
for (const [k, v] of Object.entries(map || {})) bundled.set(k, v);
|
|
14485
|
-
}
|
|
14486
|
-
var loadReport = async (rid) => {
|
|
14487
|
-
if (bundled.has(rid)) return structuredClone(bundled.get(rid));
|
|
14488
|
-
if (standalone) throw new Error(`Report "${rid}" is not loaded: pass it in reports: { "${rid}": definition }, or set server (a self-hosted ReportWright server)`);
|
|
14489
|
-
const r = await fetch(`${assetBase}/api/reports/${encodeURIComponent(rid)}`);
|
|
14490
|
-
if (!r.ok) throw new Error(`Report "${rid}" not found`);
|
|
14491
|
-
return r.json();
|
|
14492
|
-
};
|
|
14493
|
-
var fetchReport = loadReport;
|
|
14494
|
-
async function renderMain(def, params, state, reportId, exportData = false, onRetry = void 0, dataOnly = false) {
|
|
14495
|
-
await customFonts();
|
|
14496
|
-
return render(def, { parameters: params, state, reportId, exportData, dataOnly, fontStore: fontStore(), baseUrl: origin(), loadReport, fetch: reportFetch(origin(), void 0, { onRetry }) });
|
|
14497
|
-
}
|
|
14498
|
-
async function renderReport(def, params = {}, state = {}, reportId = void 0, { onPartial, exportData = false, onRetry, dataOnly = false } = {}) {
|
|
14499
|
-
const w = getWorker();
|
|
14500
|
-
if (!w) return renderMain(def, params, state, reportId, exportData, onRetry, dataOnly);
|
|
14501
|
-
const fonts = await customFonts();
|
|
14502
|
-
return new Promise((resolve, reject) => {
|
|
14503
|
-
const id = ++seq;
|
|
14504
|
-
waiting.set(id, { resolve, reject, onPartial, onRetry, retry: () => renderMain(def, params, state, reportId, exportData, onRetry, dataOnly).then(resolve, reject) });
|
|
14505
|
-
w.postMessage({ id, def, params, state, reportId, fonts, firstPages: onPartial && !dataOnly ? 2 : 0, exportData, dataOnly });
|
|
14506
|
-
});
|
|
14507
|
-
}
|
|
14508
|
-
var usingWorker = () => !!worker && !workerBroken;
|
|
14509
|
-
async function iccProfile() {
|
|
14510
|
-
const r = await fetch(fontsUrl ? besideFonts("icc/sRGB-v2-magic.icc") : `${origin()}/icc/sRGB-v2-magic.icc`);
|
|
14511
|
-
if (!r.ok) throw new Error(`the PDF/A colour profile did not load (HTTP ${r.status})`);
|
|
14512
|
-
return new Uint8Array(await r.arrayBuffer());
|
|
14513
|
-
}
|
|
14514
|
-
async function pdfBlob(model, title, o = {}) {
|
|
14515
|
-
const store = fontStore();
|
|
14516
|
-
await customFonts();
|
|
14517
|
-
await store.load(fontKeysOf(model));
|
|
14518
|
-
const { exportPdf } = await import("./chunk-5N6OFCHJ.js");
|
|
14519
|
-
const bytes = await exportPdf(model, store, {
|
|
14520
|
-
title,
|
|
14521
|
-
subset: subsetter,
|
|
14522
|
-
tagged: o.tagged,
|
|
14523
|
-
drillBase: origin(),
|
|
14524
|
-
// drill-through links open this viewer, as in the HTML export
|
|
14525
|
-
pdfa: o.pdfa ? { icc: await iccProfile() } : void 0,
|
|
14526
|
-
// a report's image: a GET under the same rules as its data (no cookies to our other routes or to other sites)
|
|
14527
|
-
fetchImage: async (src) => {
|
|
14528
|
-
try {
|
|
14529
|
-
const r = await reportFetch(origin())(src);
|
|
14530
|
-
return r.ok ? new Uint8Array(await r.arrayBuffer()) : null;
|
|
14531
|
-
} catch {
|
|
14532
|
-
return null;
|
|
14533
|
-
}
|
|
14534
|
-
}
|
|
14535
|
-
});
|
|
14536
|
-
return new Blob([bytes], { type: "application/pdf" });
|
|
14537
|
-
}
|
|
14538
|
-
|
|
14539
|
-
// src/viewer/logic.js
|
|
14540
|
-
var TOOLBAR_GROUP_KEYS = {
|
|
14541
|
-
export: (i) => i.group === "export" && i.id !== "print",
|
|
14542
|
-
search: (i) => i.group === "search",
|
|
14543
|
-
zoom: (i) => i.group === "zoom",
|
|
14544
|
-
navigation: (i) => i.group === "paging"
|
|
14545
|
-
};
|
|
14546
|
-
var TOOLBAR_ID_KEYS = { sidebar: ["thumbnails", "documentMap"] };
|
|
14547
|
-
function arrangeToolbar(builtIns, { hide = [], add: add2 = [] } = {}) {
|
|
14548
|
-
if (!hide.length && !add2.length) return builtIns;
|
|
14549
|
-
const out = [...builtIns];
|
|
14550
|
-
const taken = new Set(builtIns.map((i) => i.id));
|
|
14551
|
-
for (const a of add2) {
|
|
14552
|
-
if (!a || !a.id || taken.has(a.id)) continue;
|
|
14553
|
-
taken.add(a.id);
|
|
14554
|
-
const at = out.findIndex((i) => i.id === a.after);
|
|
14555
|
-
const item = { ...a, custom: true, group: at >= 0 ? out[at].group : `custom:${a.id}` };
|
|
14556
|
-
if (at >= 0) out.splice(at + 1, 0, item);
|
|
14557
|
-
else out.push(item);
|
|
14558
|
-
}
|
|
14559
|
-
const hiddenIds = /* @__PURE__ */ new Set();
|
|
14560
|
-
const hiddenBy = [];
|
|
14561
|
-
for (const k of hide) {
|
|
14562
|
-
if (Object.hasOwn(TOOLBAR_GROUP_KEYS, k)) hiddenBy.push(TOOLBAR_GROUP_KEYS[k]);
|
|
14563
|
-
else for (const id of Object.hasOwn(TOOLBAR_ID_KEYS, k) ? TOOLBAR_ID_KEYS[k] : [k]) hiddenIds.add(id);
|
|
14564
|
-
}
|
|
14565
|
-
let kept = out.filter((i) => i.custom || !(hiddenIds.has(i.id) || hiddenBy.some((f4) => f4(i))));
|
|
14566
|
-
if (!kept.some((i) => i.format)) kept = kept.filter((i) => i.custom || i.id !== "exportOptions");
|
|
14567
|
-
return kept;
|
|
14568
|
-
}
|
|
14569
|
-
function galleyDefinition(def) {
|
|
14570
|
-
const pg = resolvePage(def.page);
|
|
14571
|
-
if (pg.pageless) return def;
|
|
14572
|
-
const m = pg.margins;
|
|
14573
|
-
return { ...def, page: { size: "Pageless", width: pg.width, margins: [m.top, m.right, m.bottom, m.left] } };
|
|
14574
|
-
}
|
|
14575
|
-
function frozenAt(page, y) {
|
|
14576
|
-
for (const r of page?.frozen || []) if (y > r.y && y < r.end - r.h) return r;
|
|
14577
|
-
return null;
|
|
14578
|
-
}
|
|
14579
|
-
var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14580
|
-
function findText(model, q, { matchCase = false, wholeWord = false } = {}) {
|
|
14581
|
-
if (!q || !model) return [];
|
|
14582
|
-
const W2 = "[\\p{L}\\p{M}\\p{N}_]";
|
|
14583
|
-
const re = new RegExp(wholeWord ? `(?<!${W2})${escapeRe(q)}(?!${W2})` : escapeRe(q), `u${matchCase ? "" : "i"}`);
|
|
14584
|
-
const out = [];
|
|
14585
|
-
model.pages.forEach((pg, pi) => {
|
|
14586
|
-
const rows = /* @__PURE__ */ new Map();
|
|
14587
|
-
pg.items.forEach((it, k) => {
|
|
14588
|
-
if (it.t !== "text") return;
|
|
14589
|
-
for (const l of it.lines) {
|
|
14590
|
-
const key = it.rich ? `r${it.rich.id}${it.rich.l}` : `${k}${l.y}`;
|
|
14591
|
-
const r = rows.get(key);
|
|
14592
|
-
if (r) r.lines.push(l);
|
|
14593
|
-
else rows.set(key, { rich: !!it.rich, size: it.size, lines: [l] });
|
|
14594
|
-
}
|
|
14595
|
-
});
|
|
14596
|
-
for (const { rich, size, lines: row } of rows.values()) {
|
|
14597
|
-
row.sort((a, b) => a.x - b.x);
|
|
14598
|
-
const text = row.map((l) => l.text).join(rich ? "" : " ");
|
|
14599
|
-
if (!re.test(text)) continue;
|
|
14600
|
-
const x0 = row[0].x, last = row[row.length - 1];
|
|
14601
|
-
const x1 = last.x + (last.w || size * last.text.length * 0.5);
|
|
14602
|
-
const y = Math.max(...row.map((l) => l.y));
|
|
14603
|
-
out.push({ page: pi, x: x0 - 1, y: y - size * 0.95, w: x1 - x0 + 2, h: size * 1.25, text });
|
|
14604
|
-
}
|
|
14605
|
-
});
|
|
14606
|
-
return out;
|
|
14607
|
-
}
|
|
14608
|
-
function rangeValue(a, b, p) {
|
|
14609
|
-
if (a === "" && b === "") return "";
|
|
14610
|
-
const lo = a !== "" ? a : String(p.min ?? b);
|
|
14611
|
-
const hi = b !== "" ? b : String(p.max ?? a);
|
|
14612
|
-
return `${lo},${hi}`;
|
|
14613
|
-
}
|
|
14614
|
-
function pathBox(d) {
|
|
14615
|
-
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity, cx = 0, cy = 0;
|
|
14616
|
-
const add2 = (x, y) => {
|
|
14617
|
-
cx = x;
|
|
14618
|
-
cy = y;
|
|
14619
|
-
if (x < x0) x0 = x;
|
|
14620
|
-
if (x > x1) x1 = x;
|
|
14621
|
-
if (y < y0) y0 = y;
|
|
14622
|
-
if (y > y1) y1 = y;
|
|
14623
|
-
};
|
|
14624
|
-
for (const [, c, args] of String(d || "").matchAll(/([MLHVCSQTAZ])([^MLHVCSQTAZ]*)/gi)) {
|
|
14625
|
-
const n = (args.match(/-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/gi) || []).map(Number);
|
|
14626
|
-
const C = c.toUpperCase();
|
|
14627
|
-
if (C === "H") n.forEach((x) => add2(x, cy));
|
|
14628
|
-
else if (C === "V") n.forEach((y) => add2(cx, y));
|
|
14629
|
-
else if (C === "A") for (let i = 0; i + 6 < n.length; i += 7) add2(n[i + 5], n[i + 6]);
|
|
14630
|
-
else if (C !== "Z") for (let i = 0; i + 1 < n.length; i += 2) add2(n[i], n[i + 1]);
|
|
14631
|
-
}
|
|
14632
|
-
return x0 <= x1 ? { x: x0, y: y0, w: x1 - x0, h: y1 - y0 } : null;
|
|
14633
|
-
}
|
|
14634
|
-
var itemsOf = (page) => (page?.items || []).flatMap((i) => i.t === "field" ? i.draw || [] : [i]);
|
|
14635
|
-
function pageLinks(page) {
|
|
14636
|
-
const items = itemsOf(page);
|
|
14637
|
-
const texts = items.filter((i) => i.t === "text");
|
|
14638
|
-
const out = [];
|
|
14639
|
-
for (const it of items) {
|
|
14640
|
-
if (it.t !== "link" || !it.action) continue;
|
|
14641
|
-
let box = { x: it.x, y: it.y, w: it.w, h: it.h };
|
|
14642
|
-
if (it.d) {
|
|
14643
|
-
const b = pathBox(it.d);
|
|
14644
|
-
if (!b) continue;
|
|
14645
|
-
box = { ...b, x: b.x + (it.x - (it.x0 ?? it.x)), y: b.y + (it.y - (it.y0 ?? it.y)) };
|
|
14646
|
-
}
|
|
14647
|
-
if (!(box.w > 0 && box.h > 0)) continue;
|
|
14648
|
-
const words = [];
|
|
14649
|
-
for (const t of texts) for (const l of t.lines || []) {
|
|
14650
|
-
const top = l.y - (t.size || 9) * 0.9, w = l.w || (t.size || 9) * String(l.text).length * 0.5;
|
|
14651
|
-
if (l.x < box.x + box.w && l.x + w > box.x && top < box.y + box.h && l.y > box.y) words.push(String(l.text));
|
|
14652
|
-
}
|
|
14653
|
-
out.push({ action: it.action, ...box, path: !!it.d, tip: it.tip ? String(it.tip) : "", text: words.join(" ").replace(/^[▶▼]\s*/, "").replace(/\s*[↑↓]$/, "").trim() });
|
|
14654
|
-
}
|
|
14655
|
-
return out.sort((a, b) => Math.abs(a.y - b.y) > 2 ? a.y - b.y : a.x - b.x);
|
|
14656
|
-
}
|
|
14657
|
-
function readingText(page) {
|
|
14658
|
-
const rows = /* @__PURE__ */ new Map();
|
|
14659
|
-
for (const it of itemsOf(page)) {
|
|
14660
|
-
if (it.t !== "text") continue;
|
|
14661
|
-
for (const l of it.lines || []) {
|
|
14662
|
-
const k = Math.round(l.y);
|
|
14663
|
-
const r = rows.get(k);
|
|
14664
|
-
if (r) r.push(l);
|
|
14665
|
-
else rows.set(k, [l]);
|
|
14666
|
-
}
|
|
14667
|
-
}
|
|
14668
|
-
return [...rows.entries()].sort((a, b) => a[0] - b[0]).map(([, ls]) => ls.sort((a, b) => a.x - b.x).map((l) => l.text).join(" ").trim()).filter(Boolean);
|
|
14669
|
-
}
|
|
14670
|
-
var EXPORT_DEFAULTS = { pdfua: false, pdfa: false, formulas: true, html: "tables" };
|
|
14671
|
-
function exportOptionsFrom(stored) {
|
|
14672
|
-
const { v, ...rest } = stored && typeof stored === "object" ? stored : {};
|
|
14673
|
-
if (v !== 2) delete rest.html;
|
|
14674
|
-
return { ...EXPORT_DEFAULTS, ...rest };
|
|
14675
|
-
}
|
|
14676
|
-
function pruneSnapshot(snapshot, defs) {
|
|
14677
|
-
const text = defs.map((d) => JSON.stringify(d)).join("\n");
|
|
14678
|
-
const seen = /* @__PURE__ */ new Map();
|
|
14679
|
-
const used = (k) => {
|
|
14680
|
-
let u = seen.get(k);
|
|
14681
|
-
if (u === void 0) {
|
|
14682
|
-
u = text.includes(JSON.stringify(k).slice(1, -1));
|
|
14683
|
-
seen.set(k, u);
|
|
14684
|
-
}
|
|
14685
|
-
return u;
|
|
14686
|
-
};
|
|
14687
|
-
const walk = (x) => {
|
|
14688
|
-
if (Array.isArray(x)) return x.map(walk);
|
|
14689
|
-
if (!x || typeof x !== "object") return x;
|
|
14690
|
-
const o = {};
|
|
14691
|
-
for (const [k, y] of Object.entries(x)) if (y && typeof y === "object" || used(k)) o[k] = walk(y);
|
|
14692
|
-
return o;
|
|
14693
|
-
};
|
|
14694
|
-
return Object.fromEntries(Object.entries(snapshot).map(([url, x]) => [url, walk(x)]));
|
|
14695
|
-
}
|
|
14696
|
-
var SERVER_PDF_ROWS = 2e4;
|
|
14697
|
-
var SERVER_PDF_PAGES = 800;
|
|
14698
|
-
function serverPdfUrl(model, { reportId, params = {}, origin: origin2, timeZone, dirty = false }) {
|
|
14699
|
-
if (!reportId || dirty || !model) return null;
|
|
14700
|
-
const rows = Object.values(model.stats?.rows || {}).reduce((a, n) => a + (Number(n) || 0), 0);
|
|
14701
|
-
if (rows <= SERVER_PDF_ROWS && (model.pages?.length || 0) <= SERVER_PDF_PAGES) return null;
|
|
14702
|
-
const q = new URLSearchParams();
|
|
14703
|
-
for (const [k, v0] of Object.entries(params)) {
|
|
14704
|
-
const v = Array.isArray(v0) ? v0.length > 1 ? void 0 : v0[0] : v0;
|
|
14705
|
-
if (v === void 0 && Array.isArray(v0)) return null;
|
|
14706
|
-
if (v == null) continue;
|
|
14707
|
-
q.set(k, v instanceof Date ? v.toISOString() : String(v));
|
|
14708
|
-
}
|
|
14709
|
-
if (timeZone) q.set("timeZone", timeZone);
|
|
14710
|
-
const s = q.toString();
|
|
14711
|
-
return `${origin2}/api/reports/${encodeURIComponent(reportId)}/pdf${s ? `?${s}` : ""}`;
|
|
14712
|
-
}
|
|
14713
|
-
function paramChoices(def, fetched) {
|
|
14714
|
-
const out = { ...fetched };
|
|
14715
|
-
for (const p of def?.parameters || []) {
|
|
14716
|
-
if (!Array.isArray(p.options) || !p.optionsFrom || !Array.isArray(fetched?.[p.name])) continue;
|
|
14717
|
-
const fixed = p.options.map((o) => typeof o === "object" && o ? { value: o.value, label: String(o.label ?? o.value) } : { value: o, label: String(o) });
|
|
14718
|
-
const seen = new Set(fixed.map((o) => String(o.value)));
|
|
14719
|
-
out[p.name] = [...fixed, ...fetched[p.name].filter((o) => !seen.has(String(o.value)))];
|
|
14720
|
-
}
|
|
14721
|
-
return out;
|
|
14722
|
-
}
|
|
14723
|
-
|
|
14724
13816
|
export {
|
|
13817
|
+
setFetchPolicy,
|
|
13818
|
+
policyKeysIn,
|
|
13819
|
+
fetchPolicy,
|
|
14725
13820
|
reportFetch,
|
|
14726
13821
|
PAGE_SIZES,
|
|
14727
13822
|
toPt,
|
|
14728
13823
|
fromPt,
|
|
14729
13824
|
resolvePage,
|
|
14730
13825
|
normBox,
|
|
14731
|
-
TextMeasurer,
|
|
14732
|
-
finishText,
|
|
14733
13826
|
isDynamic,
|
|
14734
13827
|
evalValue,
|
|
14735
13828
|
toText,
|
|
@@ -14774,33 +13867,6 @@ export {
|
|
|
14774
13867
|
NO_CJK_FONTS,
|
|
14775
13868
|
render,
|
|
14776
13869
|
parameterOptions,
|
|
14777
|
-
resolveParameters
|
|
14778
|
-
setAssetBase,
|
|
14779
|
-
origin,
|
|
14780
|
-
setStandalone,
|
|
14781
|
-
fontsBase,
|
|
14782
|
-
subsetter,
|
|
14783
|
-
setBundledFonts,
|
|
14784
|
-
fontStore,
|
|
14785
|
-
customFonts,
|
|
14786
|
-
loadReportFonts,
|
|
14787
|
-
loadModelFonts,
|
|
14788
|
-
registerReports,
|
|
14789
|
-
fetchReport,
|
|
14790
|
-
renderReport,
|
|
14791
|
-
usingWorker,
|
|
14792
|
-
pdfBlob,
|
|
14793
|
-
arrangeToolbar,
|
|
14794
|
-
galleyDefinition,
|
|
14795
|
-
frozenAt,
|
|
14796
|
-
findText,
|
|
14797
|
-
rangeValue,
|
|
14798
|
-
pageLinks,
|
|
14799
|
-
readingText,
|
|
14800
|
-
EXPORT_DEFAULTS,
|
|
14801
|
-
exportOptionsFrom,
|
|
14802
|
-
pruneSnapshot,
|
|
14803
|
-
serverPdfUrl,
|
|
14804
|
-
paramChoices
|
|
13870
|
+
resolveParameters
|
|
14805
13871
|
};
|
|
14806
|
-
//# sourceMappingURL=chunk-
|
|
13872
|
+
//# sourceMappingURL=chunk-VNOWPSE6.js.map
|