boxpdf-html 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2838 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ fontFamily: () => fontFamily,
24
+ htmlToBoxpdf: () => htmlToBoxpdf,
25
+ parseHtml: () => parseHtml
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+
29
+ // src/css.ts
30
+ var import_css_tree = require("css-tree");
31
+
32
+ // src/color.ts
33
+ var namedColors = {
34
+ black: "#000000",
35
+ white: "#ffffff",
36
+ red: "#ff0000",
37
+ green: "#008000",
38
+ blue: "#0000ff",
39
+ transparent: "transparent"
40
+ };
41
+ function parseColor(value) {
42
+ if (!value) return void 0;
43
+ const normalized = value.trim().toLowerCase();
44
+ const resolved = namedColors[normalized] ?? normalized;
45
+ if (resolved === "transparent") return void 0;
46
+ const hex = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(resolved);
47
+ if (hex) {
48
+ const body = hex[1];
49
+ const rgb2 = body.length === 4 ? body.slice(0, 3) : body.length === 8 ? body.slice(0, 6) : body;
50
+ const full = rgb2.length === 3 ? rgb2.split("").map((c) => c + c).join("") : rgb2;
51
+ return {
52
+ r: Number.parseInt(full.slice(0, 2), 16) / 255,
53
+ g: Number.parseInt(full.slice(2, 4), 16) / 255,
54
+ b: Number.parseInt(full.slice(4, 6), 16) / 255
55
+ };
56
+ }
57
+ const rgb = parseRgbFunction(resolved);
58
+ if (rgb) return rgb;
59
+ const hsl = parseHslFunction(resolved);
60
+ if (hsl) return hsl;
61
+ const oklch = parseOklchFunction(resolved);
62
+ if (oklch) return oklch;
63
+ return void 0;
64
+ }
65
+ function parseRgbFunction(value) {
66
+ const match = /^rgba?\(\s*(.+)\s*\)$/.exec(value);
67
+ if (!match) return void 0;
68
+ const body = match[1].split("/")[0].trim();
69
+ const parts = body.includes(",") ? body.split(",") : body.split(/\s+/);
70
+ if (parts.length < 3) return void 0;
71
+ const channels = parts.slice(0, 3).map((part) => parseRgbChannel(part.trim()));
72
+ if (channels.some((channel) => channel === void 0)) return void 0;
73
+ return {
74
+ r: channels[0],
75
+ g: channels[1],
76
+ b: channels[2]
77
+ };
78
+ }
79
+ function parseRgbChannel(value) {
80
+ const percent = /^(-?[0-9.]+)%$/.exec(value);
81
+ if (percent) {
82
+ const amount2 = Number(percent[1]);
83
+ return Number.isFinite(amount2) ? clamp01(amount2 / 100) : void 0;
84
+ }
85
+ const amount = Number(value);
86
+ return Number.isFinite(amount) ? clamp255(amount) : void 0;
87
+ }
88
+ function parseHslFunction(value) {
89
+ const match = /^hsla?\(\s*(.+)\s*\)$/.exec(value);
90
+ if (!match) return void 0;
91
+ const body = match[1].split("/")[0].trim();
92
+ const parts = body.includes(",") ? body.split(",") : body.split(/\s+/);
93
+ if (parts.length < 3) return void 0;
94
+ const hue = parseHue(parts[0].trim());
95
+ const saturation = parsePercentChannel(parts[1].trim());
96
+ const lightness = parsePercentChannel(parts[2].trim());
97
+ if (hue === void 0 || saturation === void 0 || lightness === void 0) return void 0;
98
+ return hslToRgb(hue, saturation, lightness);
99
+ }
100
+ function parseHue(value) {
101
+ const match = /^(-?[0-9.]+)(deg|turn|rad)?$/.exec(value);
102
+ if (!match) return void 0;
103
+ const amount = Number(match[1]);
104
+ if (!Number.isFinite(amount)) return void 0;
105
+ const unit = match[2] ?? "deg";
106
+ if (unit === "turn") return amount * 360;
107
+ if (unit === "rad") return amount * (180 / Math.PI);
108
+ return amount;
109
+ }
110
+ function parseOklchFunction(value) {
111
+ const match = /^oklch\(\s*(.+)\s*\)$/.exec(value);
112
+ if (!match) return void 0;
113
+ const body = match[1].split("/")[0].trim();
114
+ const parts = body.split(/\s+/);
115
+ if (parts.length < 3) return void 0;
116
+ const lightness = parseOklchLightness(parts[0]);
117
+ const chroma = Number(parts[1]);
118
+ const hue = parseHue(parts[2]);
119
+ if (lightness === void 0 || !Number.isFinite(chroma) || hue === void 0) return void 0;
120
+ return oklchToRgb(lightness, chroma, hue);
121
+ }
122
+ function parseOklchLightness(value) {
123
+ const percent = /^(-?[0-9.]+)%$/.exec(value);
124
+ if (percent) {
125
+ const amount2 = Number(percent[1]);
126
+ return Number.isFinite(amount2) ? clamp01(amount2 / 100) : void 0;
127
+ }
128
+ const amount = Number(value);
129
+ return Number.isFinite(amount) ? clamp01(amount) : void 0;
130
+ }
131
+ function oklchToRgb(lightness, chroma, hue) {
132
+ const radians = hue * Math.PI / 180;
133
+ const a = chroma * Math.cos(radians);
134
+ const b = chroma * Math.sin(radians);
135
+ const lPrime = lightness + 0.3963377774 * a + 0.2158037573 * b;
136
+ const mPrime = lightness - 0.1055613458 * a - 0.0638541728 * b;
137
+ const sPrime = lightness - 0.0894841775 * a - 1.291485548 * b;
138
+ const l = lPrime ** 3;
139
+ const m = mPrime ** 3;
140
+ const s = sPrime ** 3;
141
+ return {
142
+ r: linearSrgbToRgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),
143
+ g: linearSrgbToRgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),
144
+ b: linearSrgbToRgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s)
145
+ };
146
+ }
147
+ function linearSrgbToRgb(value) {
148
+ const converted = value <= 31308e-7 ? 12.92 * value : 1.055 * value ** (1 / 2.4) - 0.055;
149
+ return clamp01(converted);
150
+ }
151
+ function parsePercentChannel(value) {
152
+ const match = /^(-?[0-9.]+)%$/.exec(value);
153
+ if (!match) return void 0;
154
+ const amount = Number(match[1]);
155
+ return Number.isFinite(amount) ? clamp01(amount / 100) : void 0;
156
+ }
157
+ function hslToRgb(hue, saturation, lightness) {
158
+ const normalizedHue = (hue % 360 + 360) % 360 / 360;
159
+ if (saturation === 0) return { r: lightness, g: lightness, b: lightness };
160
+ const q = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;
161
+ const p = 2 * lightness - q;
162
+ return {
163
+ r: hueToRgb(p, q, normalizedHue + 1 / 3),
164
+ g: hueToRgb(p, q, normalizedHue),
165
+ b: hueToRgb(p, q, normalizedHue - 1 / 3)
166
+ };
167
+ }
168
+ function hueToRgb(p, q, t) {
169
+ let value = t;
170
+ if (value < 0) value += 1;
171
+ if (value > 1) value -= 1;
172
+ if (value < 1 / 6) return p + (q - p) * 6 * value;
173
+ if (value < 1 / 2) return q;
174
+ if (value < 2 / 3) return p + (q - p) * (2 / 3 - value) * 6;
175
+ return p;
176
+ }
177
+ function clamp01(value) {
178
+ return Math.max(0, Math.min(1, value));
179
+ }
180
+ function clamp255(value) {
181
+ return clamp01(value / 255);
182
+ }
183
+
184
+ // src/units.ts
185
+ var ROOT_FONT_SIZE = 12;
186
+ var VIEWPORT_WIDTH = 612;
187
+ var VIEWPORT_HEIGHT = 792;
188
+ function parseLength(value, fontSize) {
189
+ if (!value) return void 0;
190
+ const normalized = value.trim().toLowerCase();
191
+ if (normalized === "auto" || normalized.endsWith("%")) return void 0;
192
+ if (isCalc(normalized)) {
193
+ const parsed = parseLengthPercentage(normalized, fontSize);
194
+ return parsed && parsed.percent === 0 ? parsed.length : void 0;
195
+ }
196
+ return parseSimpleLength(normalized, fontSize);
197
+ }
198
+ function parsePercentage(value) {
199
+ if (!value) return void 0;
200
+ const normalized = value.trim().toLowerCase();
201
+ if (isCalc(normalized)) {
202
+ const parsed = parseLengthPercentage(normalized, 12);
203
+ return parsed && parsed.length === 0 ? parsed.percent : void 0;
204
+ }
205
+ const match = /^(-?[0-9.]+)%$/.exec(normalized);
206
+ if (!match) return void 0;
207
+ const amount = Number(match[1]);
208
+ return Number.isFinite(amount) ? amount / 100 : void 0;
209
+ }
210
+ function parseLengthPercentage(value, fontSize) {
211
+ if (!value) return void 0;
212
+ const normalized = value.trim().toLowerCase();
213
+ return parseCalcLengthPercentage(normalized, fontSize);
214
+ }
215
+ function parseLineHeight(value, fontSize) {
216
+ if (!value) return void 0;
217
+ const normalized = value.trim().toLowerCase();
218
+ if (normalized === "normal") return void 0;
219
+ const unitless = /^([0-9.]+)$/.exec(normalized);
220
+ if (unitless) return Number(unitless[1]) * fontSize;
221
+ if (isCalc(normalized)) {
222
+ const number = parseCalcNumber(normalized);
223
+ if (number !== void 0) return number * fontSize;
224
+ }
225
+ return parseLength(normalized, fontSize);
226
+ }
227
+ function parseLineHeightScale(value) {
228
+ if (!value) return void 0;
229
+ const normalized = value.trim().toLowerCase();
230
+ const unitless = /^([0-9.]+)$/.exec(normalized);
231
+ if (unitless) return Number(unitless[1]);
232
+ if (!isCalc(normalized)) return void 0;
233
+ return parseCalcNumber(normalized);
234
+ }
235
+ function isCalc(value) {
236
+ return /^calc\(.+\)$/.test(value);
237
+ }
238
+ function parseSimplePercentage(value) {
239
+ const match = /^(-?[0-9.]+)%$/.exec(value);
240
+ if (!match) return void 0;
241
+ const amount = Number(match[1]);
242
+ return Number.isFinite(amount) ? amount / 100 : void 0;
243
+ }
244
+ function parseSimpleLength(value, fontSize) {
245
+ const match = /^(-?[0-9.]+)(px|pt|em|rem|vw|vh)?$/.exec(value);
246
+ if (!match) return void 0;
247
+ const amount = Number(match[1]);
248
+ if (!Number.isFinite(amount)) return void 0;
249
+ const unit = match[2] ?? "px";
250
+ if (unit === "pt") return amount;
251
+ if (unit === "em") return amount * fontSize;
252
+ if (unit === "rem") return amount * ROOT_FONT_SIZE;
253
+ if (unit === "vw") return amount / 100 * VIEWPORT_WIDTH;
254
+ if (unit === "vh") return amount / 100 * VIEWPORT_HEIGHT;
255
+ return amount * 0.75;
256
+ }
257
+ function parseCalcLengthPercentage(value, fontSize) {
258
+ const stripped = stripCalc(value.trim());
259
+ const simplePercent = parseSimplePercentage(stripped);
260
+ if (simplePercent !== void 0) return { length: 0, percent: simplePercent };
261
+ const simpleLength = parseSimpleLength(stripped, fontSize);
262
+ if (simpleLength !== void 0) return { length: simpleLength, percent: 0 };
263
+ const sum = topLevelOperator(stripped, ["+", "-"]);
264
+ if (sum) {
265
+ const left = parseCalcLengthPercentage(stripped.slice(0, sum.index), fontSize);
266
+ const right = parseCalcLengthPercentage(stripped.slice(sum.index + 1), fontSize);
267
+ if (!left || !right) return void 0;
268
+ const sign = sum.operator === "-" ? -1 : 1;
269
+ return { length: left.length + sign * right.length, percent: left.percent + sign * right.percent };
270
+ }
271
+ const product = topLevelOperator(stripped, ["*", "/"]);
272
+ if (product) {
273
+ const leftRaw = stripped.slice(0, product.index);
274
+ const rightRaw = stripped.slice(product.index + 1);
275
+ const leftLength = parseCalcLengthPercentage(leftRaw, fontSize);
276
+ const rightLength = parseCalcLengthPercentage(rightRaw, fontSize);
277
+ const leftNumber = parseCalcNumber(leftRaw);
278
+ const rightNumber = parseCalcNumber(rightRaw);
279
+ if (product.operator === "*" && leftLength && rightNumber !== void 0) {
280
+ return { length: leftLength.length * rightNumber, percent: leftLength.percent * rightNumber };
281
+ }
282
+ if (product.operator === "*" && rightLength && leftNumber !== void 0) {
283
+ return { length: rightLength.length * leftNumber, percent: rightLength.percent * leftNumber };
284
+ }
285
+ if (product.operator === "/" && leftLength && rightNumber !== void 0 && rightNumber !== 0) {
286
+ return { length: leftLength.length / rightNumber, percent: leftLength.percent / rightNumber };
287
+ }
288
+ }
289
+ return void 0;
290
+ }
291
+ function parseCalcNumber(value) {
292
+ const stripped = stripCalc(value.trim());
293
+ const sum = topLevelOperator(stripped, ["+", "-"]);
294
+ if (sum) {
295
+ const left = parseCalcNumber(stripped.slice(0, sum.index));
296
+ const right = parseCalcNumber(stripped.slice(sum.index + 1));
297
+ if (left === void 0 || right === void 0) return void 0;
298
+ return sum.operator === "-" ? left - right : left + right;
299
+ }
300
+ const product = topLevelOperator(stripped, ["*", "/"]);
301
+ if (product) {
302
+ const left = parseCalcNumber(stripped.slice(0, product.index));
303
+ const right = parseCalcNumber(stripped.slice(product.index + 1));
304
+ if (left === void 0 || right === void 0) return void 0;
305
+ return product.operator === "/" ? right === 0 ? void 0 : left / right : left * right;
306
+ }
307
+ const number = Number(stripped);
308
+ return Number.isFinite(number) ? number : void 0;
309
+ }
310
+ function stripCalc(value) {
311
+ let out = value.trim();
312
+ while (isCalc(out)) out = out.slice(5, -1).trim();
313
+ return out;
314
+ }
315
+ function topLevelOperator(value, operators) {
316
+ let depth = 0;
317
+ for (let index = value.length - 1; index >= 0; index -= 1) {
318
+ const char = value[index];
319
+ if (char === ")") depth += 1;
320
+ else if (char === "(") depth = Math.max(0, depth - 1);
321
+ else if (depth === 0 && operators.includes(char)) {
322
+ const prev = value[index - 1];
323
+ if ((char === "+" || char === "-") && (index === 0 || prev === void 0 || /[+\-*/(]/.test(prev))) continue;
324
+ return { operator: char, index };
325
+ }
326
+ }
327
+ return void 0;
328
+ }
329
+
330
+ // src/css.ts
331
+ function parseStylesheets(stylesheets) {
332
+ let order = 0;
333
+ const rules = [];
334
+ for (const css of stylesheets) {
335
+ const ast = (0, import_css_tree.parse)(css, { parseValue: false, parseCustomProperty: false });
336
+ (0, import_css_tree.walk)(ast, (node) => {
337
+ if (node.type !== "Rule") return;
338
+ const prelude = node.prelude;
339
+ const block = node.block;
340
+ if (!prelude || !block) return;
341
+ const declarations = rawDeclarationsFromBlock(block);
342
+ for (const selector of selectorList(prelude)) {
343
+ rules.push({
344
+ selector,
345
+ declarations: declarations.declarations.map((declaration) => ({ ...declaration, selector })),
346
+ importantDeclarations: declarations.importantDeclarations.map((declaration) => ({ ...declaration, selector })),
347
+ specificity: specificity(selector),
348
+ order: order++
349
+ });
350
+ }
351
+ });
352
+ }
353
+ return rules;
354
+ }
355
+ function parseStyleAttribute(value, fontSize, customProperties = {}, unsupportedCss) {
356
+ const declarations = { declarations: {}, importantDeclarations: {} };
357
+ if (!value) return declarations;
358
+ const raw = rawDeclarationsFromStyleAttribute(value);
359
+ const vars = mergeCustomProperties(customProperties, raw.declarations, raw.importantDeclarations);
360
+ parseDeclarationsInto(declarations.declarations, raw.declarations, fontSize, vars, unsupportedCss);
361
+ parseDeclarationsInto(declarations.importantDeclarations, raw.importantDeclarations, fontSize, vars, unsupportedCss);
362
+ return declarations;
363
+ }
364
+ function ruleDeclarationsFor(node, rules, fontSize, customProperties = {}, unsupportedCss) {
365
+ const out = { declarations: {}, importantDeclarations: {} };
366
+ const declarations = [];
367
+ const importantDeclarations = [];
368
+ for (const rule of rules.filter((r) => matchesSelector(node, r.selector)).sort(compareRule)) {
369
+ declarations.push(...rule.declarations);
370
+ importantDeclarations.push(...rule.importantDeclarations);
371
+ }
372
+ const vars = mergeCustomProperties(customProperties, declarations, importantDeclarations);
373
+ parseDeclarationsInto(out.declarations, declarations, fontSize, vars, unsupportedCss);
374
+ parseDeclarationsInto(out.importantDeclarations, importantDeclarations, fontSize, vars, unsupportedCss);
375
+ return out;
376
+ }
377
+ function rawDeclarationsFromBlock(block) {
378
+ const out = { declarations: [], importantDeclarations: [] };
379
+ const children = block.children;
380
+ children?.forEach((node) => {
381
+ if (node.type !== "Declaration") return;
382
+ const target = node.important ? out.importantDeclarations : out.declarations;
383
+ target.push({ property: String(node.property), value: (0, import_css_tree.generate)(node.value) });
384
+ });
385
+ return out;
386
+ }
387
+ function rawDeclarationsFromStyleAttribute(value) {
388
+ const out = { declarations: [], importantDeclarations: [] };
389
+ for (const chunk of value.split(";")) {
390
+ const colon = chunk.indexOf(":");
391
+ if (colon === -1) continue;
392
+ const parsed = stripImportant(chunk.slice(colon + 1).trim());
393
+ const target = parsed.important ? out.importantDeclarations : out.declarations;
394
+ target.push({ property: chunk.slice(0, colon).trim(), value: parsed.value });
395
+ }
396
+ return out;
397
+ }
398
+ function parseDeclarationsInto(out, declarations, fontSize, customProperties, unsupportedCss) {
399
+ const vars = customPropertiesFrom(declarations, customProperties);
400
+ for (const declaration of declarations) {
401
+ const property = declaration.property.trim();
402
+ if (property.startsWith("--")) continue;
403
+ if (!isSupportedCssProperty(property)) {
404
+ if (shouldReportUnsupportedCss(declaration)) unsupportedCss?.(declaration);
405
+ continue;
406
+ }
407
+ applyDeclaration(out, property, resolveVars(declaration.value, vars), fontSize);
408
+ }
409
+ if (Object.keys(vars).length > 0) out.customProperties = vars;
410
+ }
411
+ function shouldReportUnsupportedCss(declaration) {
412
+ return declaration.selector === void 0 || declaration.selector.includes(".");
413
+ }
414
+ var supportedCssProperties = /* @__PURE__ */ new Set([
415
+ "display",
416
+ "float",
417
+ "order",
418
+ "flex-direction",
419
+ "align-items",
420
+ "justify-content",
421
+ "color",
422
+ "background",
423
+ "background-color",
424
+ "background-image",
425
+ "background-size",
426
+ "background-repeat",
427
+ "background-position",
428
+ "object-fit",
429
+ "overflow",
430
+ "overflow-x",
431
+ "overflow-y",
432
+ "font-size",
433
+ "font",
434
+ "font-family",
435
+ "font-weight",
436
+ "font-style",
437
+ "line-height",
438
+ "white-space",
439
+ "text-align",
440
+ "text-decoration",
441
+ "text-decoration-line",
442
+ "text-transform",
443
+ "text-indent",
444
+ "vertical-align",
445
+ "box-sizing",
446
+ "position",
447
+ "top",
448
+ "right",
449
+ "bottom",
450
+ "left",
451
+ "inset",
452
+ "inset-block",
453
+ "inset-inline",
454
+ "inset-block-start",
455
+ "inset-block-end",
456
+ "inset-inline-start",
457
+ "inset-inline-end",
458
+ "z-index",
459
+ "list-style",
460
+ "list-style-type",
461
+ "width",
462
+ "min-width",
463
+ "max-width",
464
+ "height",
465
+ "aspect-ratio",
466
+ "margin",
467
+ "margin-block",
468
+ "margin-inline",
469
+ "margin-block-start",
470
+ "margin-block-end",
471
+ "margin-inline-start",
472
+ "margin-inline-end",
473
+ "margin-top",
474
+ "margin-right",
475
+ "margin-bottom",
476
+ "margin-left",
477
+ "padding",
478
+ "padding-block",
479
+ "padding-inline",
480
+ "padding-block-start",
481
+ "padding-block-end",
482
+ "padding-inline-start",
483
+ "padding-inline-end",
484
+ "padding-top",
485
+ "padding-right",
486
+ "padding-bottom",
487
+ "padding-left",
488
+ "gap",
489
+ "column-gap",
490
+ "grid-column-gap",
491
+ "row-gap",
492
+ "grid-row-gap",
493
+ "grid-template-columns",
494
+ "grid-column",
495
+ "grid-column-start",
496
+ "grid-column-end",
497
+ "border",
498
+ "border-top",
499
+ "border-right",
500
+ "border-bottom",
501
+ "border-left",
502
+ "border-block",
503
+ "border-inline",
504
+ "border-block-start",
505
+ "border-block-end",
506
+ "border-inline-start",
507
+ "border-inline-end",
508
+ "border-style",
509
+ "border-top-style",
510
+ "border-right-style",
511
+ "border-bottom-style",
512
+ "border-left-style",
513
+ "border-block-start-style",
514
+ "border-block-end-style",
515
+ "border-inline-start-style",
516
+ "border-inline-end-style",
517
+ "border-width",
518
+ "border-top-width",
519
+ "border-right-width",
520
+ "border-bottom-width",
521
+ "border-left-width",
522
+ "border-block-start-width",
523
+ "border-block-end-width",
524
+ "border-inline-start-width",
525
+ "border-inline-end-width",
526
+ "border-color",
527
+ "border-top-color",
528
+ "border-right-color",
529
+ "border-bottom-color",
530
+ "border-left-color",
531
+ "border-block-start-color",
532
+ "border-block-end-color",
533
+ "border-inline-start-color",
534
+ "border-inline-end-color",
535
+ "border-radius",
536
+ "border-collapse"
537
+ ]);
538
+ function isSupportedCssProperty(property) {
539
+ return supportedCssProperties.has(property.trim().toLowerCase());
540
+ }
541
+ function mergeCustomProperties(inherited, declarations, importantDeclarations) {
542
+ return customPropertiesFrom(importantDeclarations, customPropertiesFrom(declarations, inherited));
543
+ }
544
+ function customPropertiesFrom(declarations, inherited) {
545
+ let out = inherited;
546
+ for (const declaration of declarations) {
547
+ const property = declaration.property.trim();
548
+ if (!property.startsWith("--")) continue;
549
+ if (out === inherited) out = { ...inherited };
550
+ out[property] = declaration.value.trim();
551
+ }
552
+ return out;
553
+ }
554
+ function resolveVars(value, customProperties, seen = /* @__PURE__ */ new Set()) {
555
+ let out = "";
556
+ let index = 0;
557
+ while (index < value.length) {
558
+ const start = value.indexOf("var(", index);
559
+ if (start === -1) {
560
+ out += value.slice(index);
561
+ break;
562
+ }
563
+ out += value.slice(index, start);
564
+ const end = closingParenIndex(value, start + 4);
565
+ if (end === -1) {
566
+ out += value.slice(start);
567
+ break;
568
+ }
569
+ out += resolveVarFunction(value.slice(start + 4, end), customProperties, seen);
570
+ index = end + 1;
571
+ }
572
+ return out;
573
+ }
574
+ function resolveVarFunction(args, customProperties, seen) {
575
+ const comma = topLevelCommaIndex(args);
576
+ const name = (comma === -1 ? args : args.slice(0, comma)).trim();
577
+ if (!/^--[A-Za-z0-9_-]+$/.test(name)) return "";
578
+ const fallback = comma === -1 ? void 0 : args.slice(comma + 1).trim();
579
+ if (seen.has(name)) return fallback ? resolveVars(fallback, customProperties, seen) : "";
580
+ const replacement = customProperties[name];
581
+ if (replacement === void 0) return fallback ? resolveVars(fallback, customProperties, seen) : "";
582
+ const nextSeen = new Set(seen);
583
+ nextSeen.add(name);
584
+ return resolveVars(replacement, customProperties, nextSeen);
585
+ }
586
+ function closingParenIndex(value, openIndex) {
587
+ let depth = 0;
588
+ for (let index = openIndex; index < value.length; index += 1) {
589
+ const char = value[index];
590
+ if (char === "(") depth += 1;
591
+ if (char === ")") {
592
+ if (depth === 0) return index;
593
+ depth -= 1;
594
+ }
595
+ }
596
+ return -1;
597
+ }
598
+ function topLevelCommaIndex(value) {
599
+ let depth = 0;
600
+ for (let index = 0; index < value.length; index += 1) {
601
+ const char = value[index];
602
+ if (char === "(") depth += 1;
603
+ if (char === ")") depth = Math.max(0, depth - 1);
604
+ if (char === "," && depth === 0) return index;
605
+ }
606
+ return -1;
607
+ }
608
+ function stripImportant(value) {
609
+ if (!/!\s*important\s*$/i.test(value)) return { value, important: false };
610
+ return { value: value.replace(/!\s*important\s*$/i, "").trim(), important: true };
611
+ }
612
+ function applyDeclaration(out, property, rawValue, fontSize) {
613
+ const value = rawValue.trim().toLowerCase();
614
+ switch (property.trim().toLowerCase()) {
615
+ case "display":
616
+ if (value === "flow-root" || value === "list-item") out.display = "block";
617
+ else if (["block", "inline", "inline-block", "flex", "inline-flex", "grid", "inline-grid", "contents", "none"].includes(value)) {
618
+ out.display = value;
619
+ }
620
+ break;
621
+ case "float":
622
+ if (value === "none" || value === "left" || value === "right") out.float = value;
623
+ break;
624
+ case "order": {
625
+ const order = Number.parseInt(value, 10);
626
+ if (Number.isFinite(order)) out.order = order;
627
+ break;
628
+ }
629
+ case "flex-direction":
630
+ if (value === "row" || value === "row-reverse" || value === "column" || value === "column-reverse") out.flexDirection = value;
631
+ break;
632
+ case "align-items":
633
+ if (value === "flex-start") out.alignItems = "start";
634
+ else if (value === "flex-end") out.alignItems = "end";
635
+ else if (["start", "center", "end", "stretch", "baseline"].includes(value)) {
636
+ out.alignItems = value;
637
+ }
638
+ break;
639
+ case "justify-content":
640
+ if (value === "flex-start") out.justifyContent = "start";
641
+ else if (value === "flex-end") out.justifyContent = "end";
642
+ else if (value === "space-between") out.justifyContent = "between";
643
+ else if (value === "space-around") out.justifyContent = "around";
644
+ else if (value === "space-evenly") out.justifyContent = "evenly";
645
+ else if (["start", "center", "end"].includes(value)) out.justifyContent = value;
646
+ break;
647
+ case "color":
648
+ out.color = parseColor(value);
649
+ break;
650
+ case "background":
651
+ parseBackground(out, rawValue);
652
+ break;
653
+ case "background-color":
654
+ out.background = parseColor(value);
655
+ break;
656
+ case "background-image":
657
+ out.backgroundImageUrl = parseBackgroundImageUrl(rawValue);
658
+ break;
659
+ case "background-size":
660
+ if (value === "auto" || value === "cover" || value === "contain") out.backgroundSize = value;
661
+ break;
662
+ case "background-repeat":
663
+ if (value === "repeat" || value === "repeat-x" || value === "repeat-y" || value === "no-repeat") out.backgroundRepeat = value;
664
+ break;
665
+ case "background-position":
666
+ parseBackgroundPosition(out, value);
667
+ break;
668
+ case "object-fit":
669
+ if (value === "fill" || value === "contain" || value === "cover") out.objectFit = value;
670
+ break;
671
+ case "overflow":
672
+ case "overflow-x":
673
+ case "overflow-y":
674
+ if (value === "hidden" || value === "clip") out.overflow = "hidden";
675
+ else if (value === "visible") out.overflow = "visible";
676
+ break;
677
+ case "font-size":
678
+ out.fontSize = parseLength(value, fontSize) ?? out.fontSize;
679
+ break;
680
+ case "font":
681
+ parseFontShorthand(out, rawValue, fontSize);
682
+ break;
683
+ case "font-family":
684
+ out.fontFamily = parseFontFamily(rawValue);
685
+ break;
686
+ case "font-weight":
687
+ out.fontWeight = parseFontWeight(value);
688
+ break;
689
+ case "font-style":
690
+ out.fontStyle = value === "italic" ? "italic" : "normal";
691
+ break;
692
+ case "line-height":
693
+ {
694
+ const scale = parseLineHeightScale(value);
695
+ if (scale !== void 0) {
696
+ out.lineHeightScale = scale;
697
+ out.lineHeight = void 0;
698
+ } else {
699
+ out.lineHeight = parseLineHeight(value, fontSize);
700
+ out.lineHeightScale = void 0;
701
+ }
702
+ }
703
+ break;
704
+ case "white-space":
705
+ if (value === "normal" || value === "nowrap" || value === "pre" || value === "pre-wrap" || value === "pre-line") {
706
+ out.whiteSpace = value;
707
+ }
708
+ break;
709
+ case "text-align":
710
+ if (value === "left" || value === "center" || value === "right") out.textAlign = value;
711
+ break;
712
+ case "text-decoration":
713
+ case "text-decoration-line":
714
+ out.textDecorationLine = parseTextDecoration(value);
715
+ break;
716
+ case "text-transform":
717
+ if (value === "none" || value === "uppercase" || value === "lowercase" || value === "capitalize") {
718
+ out.textTransform = value;
719
+ }
720
+ break;
721
+ case "text-indent":
722
+ out.textIndent = parseLength(value, fontSize);
723
+ break;
724
+ case "vertical-align":
725
+ if (value === "middle" || value === "baseline") out.verticalAlign = value;
726
+ break;
727
+ case "box-sizing":
728
+ if (value === "content-box" || value === "border-box") out.boxSizing = value;
729
+ break;
730
+ case "position":
731
+ if (value === "relative" || value === "absolute") out.position = value;
732
+ break;
733
+ case "top":
734
+ case "right":
735
+ case "bottom":
736
+ case "left":
737
+ out[property] = parseLength(value, fontSize);
738
+ break;
739
+ case "inset":
740
+ applyInset(out, value, fontSize);
741
+ break;
742
+ case "inset-block":
743
+ applyTwoValueInset(out, "top", "bottom", value, fontSize);
744
+ break;
745
+ case "inset-inline":
746
+ applyTwoValueInset(out, "left", "right", value, fontSize);
747
+ break;
748
+ case "inset-block-start":
749
+ out.top = parseLength(value, fontSize);
750
+ break;
751
+ case "inset-block-end":
752
+ out.bottom = parseLength(value, fontSize);
753
+ break;
754
+ case "inset-inline-start":
755
+ out.left = parseLength(value, fontSize);
756
+ break;
757
+ case "inset-inline-end":
758
+ out.right = parseLength(value, fontSize);
759
+ break;
760
+ case "z-index": {
761
+ const zIndex = Number.parseInt(value, 10);
762
+ if (Number.isFinite(zIndex)) out.zIndex = zIndex;
763
+ break;
764
+ }
765
+ case "list-style":
766
+ case "list-style-type":
767
+ if (value.includes("none")) out.listStyleType = "none";
768
+ else if (value.includes("decimal")) out.listStyleType = "decimal";
769
+ else if (value.includes("disc")) out.listStyleType = "disc";
770
+ break;
771
+ case "width":
772
+ setLengthPercentage(out, "width", value, fontSize);
773
+ break;
774
+ case "min-width":
775
+ setLengthPercentage(out, "minWidth", value, fontSize);
776
+ break;
777
+ case "max-width":
778
+ if (value === "none") {
779
+ out.maxWidth = void 0;
780
+ out.maxWidthPercent = void 0;
781
+ out.maxWidthCalc = void 0;
782
+ } else {
783
+ setLengthPercentage(out, "maxWidth", value, fontSize);
784
+ }
785
+ break;
786
+ case "height":
787
+ out.height = parseLength(value, fontSize);
788
+ break;
789
+ case "aspect-ratio":
790
+ out.aspectRatio = parseAspectRatio(value);
791
+ break;
792
+ case "margin":
793
+ applyMargin(out, value, fontSize);
794
+ break;
795
+ case "margin-block":
796
+ applyTwoValueEdges(out, "margin", "top", "bottom", value, fontSize);
797
+ break;
798
+ case "margin-inline":
799
+ applyTwoValueMargin(out, "left", "right", value, fontSize);
800
+ break;
801
+ case "margin-block-start":
802
+ out.margin = setEdge(out.margin, "top", parseLength(value, fontSize));
803
+ break;
804
+ case "margin-block-end":
805
+ out.margin = setEdge(out.margin, "bottom", parseLength(value, fontSize));
806
+ break;
807
+ case "margin-inline-start":
808
+ setMarginEdge(out, "left", value, fontSize);
809
+ break;
810
+ case "margin-inline-end":
811
+ setMarginEdge(out, "right", value, fontSize);
812
+ break;
813
+ case "margin-top":
814
+ case "margin-right":
815
+ case "margin-bottom":
816
+ case "margin-left":
817
+ setMarginEdge(out, property.slice("margin-".length), value, fontSize);
818
+ break;
819
+ case "padding":
820
+ out.padding = parseEdges(value, fontSize);
821
+ break;
822
+ case "padding-block":
823
+ applyTwoValueEdges(out, "padding", "top", "bottom", value, fontSize);
824
+ break;
825
+ case "padding-inline":
826
+ applyTwoValueEdges(out, "padding", "left", "right", value, fontSize);
827
+ break;
828
+ case "padding-block-start":
829
+ out.padding = setEdge(out.padding, "top", parseLength(value, fontSize));
830
+ break;
831
+ case "padding-block-end":
832
+ out.padding = setEdge(out.padding, "bottom", parseLength(value, fontSize));
833
+ break;
834
+ case "padding-inline-start":
835
+ out.padding = setEdge(out.padding, "left", parseLength(value, fontSize));
836
+ break;
837
+ case "padding-inline-end":
838
+ out.padding = setEdge(out.padding, "right", parseLength(value, fontSize));
839
+ break;
840
+ case "padding-top":
841
+ case "padding-right":
842
+ case "padding-bottom":
843
+ case "padding-left":
844
+ out.padding = setEdge(out.padding, property.slice("padding-".length), parseLength(value, fontSize));
845
+ break;
846
+ case "gap":
847
+ out.gap = parseLength(value, fontSize);
848
+ break;
849
+ case "column-gap":
850
+ case "grid-column-gap":
851
+ out.columnGap = parseLength(value, fontSize);
852
+ break;
853
+ case "row-gap":
854
+ case "grid-row-gap":
855
+ out.rowGap = parseLength(value, fontSize);
856
+ break;
857
+ case "grid-template-columns":
858
+ out.gridTemplateColumns = parseGridTemplateColumns(value, fontSize);
859
+ break;
860
+ case "grid-column":
861
+ parseGridColumn(out, value);
862
+ break;
863
+ case "grid-column-start":
864
+ parseGridColumnLine(out, "gridColumnStart", value);
865
+ break;
866
+ case "grid-column-end":
867
+ parseGridColumnLine(out, "gridColumnEnd", value);
868
+ break;
869
+ case "border":
870
+ parseBorder(out, value, fontSize);
871
+ break;
872
+ case "border-style":
873
+ case "border-top-style":
874
+ case "border-right-style":
875
+ case "border-bottom-style":
876
+ case "border-left-style":
877
+ case "border-block-start-style":
878
+ case "border-block-end-style":
879
+ case "border-inline-start-style":
880
+ case "border-inline-end-style":
881
+ break;
882
+ case "border-top":
883
+ case "border-right":
884
+ case "border-bottom":
885
+ case "border-left":
886
+ setBorderSide(out, property.slice("border-".length), parseBorderValue(value, fontSize));
887
+ break;
888
+ case "border-block":
889
+ setBorderSide(out, "top", parseBorderValue(value, fontSize));
890
+ setBorderSide(out, "bottom", parseBorderValue(value, fontSize));
891
+ break;
892
+ case "border-inline":
893
+ setBorderSide(out, "left", parseBorderValue(value, fontSize));
894
+ setBorderSide(out, "right", parseBorderValue(value, fontSize));
895
+ break;
896
+ case "border-block-start":
897
+ setBorderSide(out, "top", parseBorderValue(value, fontSize));
898
+ break;
899
+ case "border-block-end":
900
+ setBorderSide(out, "bottom", parseBorderValue(value, fontSize));
901
+ break;
902
+ case "border-inline-start":
903
+ setBorderSide(out, "left", parseBorderValue(value, fontSize));
904
+ break;
905
+ case "border-inline-end":
906
+ setBorderSide(out, "right", parseBorderValue(value, fontSize));
907
+ break;
908
+ case "border-width":
909
+ out.borderWidth = parseLength(value, fontSize);
910
+ break;
911
+ case "border-top-width":
912
+ case "border-right-width":
913
+ case "border-bottom-width":
914
+ case "border-left-width":
915
+ setBorderSide(out, borderSideFromProperty(property, "-width"), { width: parseLength(value, fontSize) });
916
+ break;
917
+ case "border-block-start-width":
918
+ setBorderSide(out, "top", { width: parseLength(value, fontSize) });
919
+ break;
920
+ case "border-block-end-width":
921
+ setBorderSide(out, "bottom", { width: parseLength(value, fontSize) });
922
+ break;
923
+ case "border-inline-start-width":
924
+ setBorderSide(out, "left", { width: parseLength(value, fontSize) });
925
+ break;
926
+ case "border-inline-end-width":
927
+ setBorderSide(out, "right", { width: parseLength(value, fontSize) });
928
+ break;
929
+ case "border-color":
930
+ out.borderColor = parseColor(value);
931
+ break;
932
+ case "border-top-color":
933
+ case "border-right-color":
934
+ case "border-bottom-color":
935
+ case "border-left-color":
936
+ setBorderSide(out, borderSideFromProperty(property, "-color"), { color: parseColor(value) });
937
+ break;
938
+ case "border-block-start-color":
939
+ setBorderSide(out, "top", { color: parseColor(value) });
940
+ break;
941
+ case "border-block-end-color":
942
+ setBorderSide(out, "bottom", { color: parseColor(value) });
943
+ break;
944
+ case "border-inline-start-color":
945
+ setBorderSide(out, "left", { color: parseColor(value) });
946
+ break;
947
+ case "border-inline-end-color":
948
+ setBorderSide(out, "right", { color: parseColor(value) });
949
+ break;
950
+ case "border-radius":
951
+ out.borderRadius = parseLength(value.split(/\s+/)[0], fontSize);
952
+ break;
953
+ case "border-collapse":
954
+ if (value === "collapse" || value === "separate") out.borderCollapse = value;
955
+ break;
956
+ }
957
+ }
958
+ function parseAspectRatio(value) {
959
+ const normalized = value.replace(/\bauto\b/g, "").trim();
960
+ if (!normalized) return void 0;
961
+ const slash = /^([0-9.]+)\s*\/\s*([0-9.]+)$/.exec(normalized);
962
+ if (slash) {
963
+ const width = Number(slash[1]);
964
+ const height = Number(slash[2]);
965
+ return Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0 ? width / height : void 0;
966
+ }
967
+ const ratio = Number(normalized);
968
+ return Number.isFinite(ratio) && ratio > 0 ? ratio : void 0;
969
+ }
970
+ function setLengthPercentage(out, property, value, fontSize) {
971
+ const parsed = parseLengthPercentage(value, fontSize);
972
+ const percentKey = `${property}Percent`;
973
+ const calcKey = `${property}Calc`;
974
+ out[property] = void 0;
975
+ out[percentKey] = void 0;
976
+ out[calcKey] = void 0;
977
+ if (!parsed) return;
978
+ if (parsed.percent !== 0 && parsed.length !== 0) out[calcKey] = parsed;
979
+ else if (parsed.percent !== 0) out[percentKey] = parsed.percent;
980
+ else out[property] = parsed.length;
981
+ }
982
+ function applyInset(out, value, fontSize) {
983
+ const lengths = cssValueTokens(value).map((part) => parseLength(part, fontSize));
984
+ if (lengths.some((length) => length === void 0)) return;
985
+ const [top, right = top, bottom = top, left = right] = lengths;
986
+ out.top = top;
987
+ out.right = right;
988
+ out.bottom = bottom;
989
+ out.left = left;
990
+ }
991
+ function applyTwoValueInset(out, firstSide, secondSide, value, fontSize) {
992
+ const [firstRaw, secondRaw = firstRaw] = cssValueTokens(value);
993
+ const first = parseLength(firstRaw, fontSize);
994
+ const second = parseLength(secondRaw, fontSize);
995
+ if (first !== void 0) out[firstSide] = first;
996
+ if (second !== void 0) out[secondSide] = second;
997
+ }
998
+ function applyTwoValueEdges(out, property, firstSide, secondSide, value, fontSize) {
999
+ const [firstRaw, secondRaw = firstRaw] = cssValueTokens(value);
1000
+ out[property] = setEdge(out[property], firstSide, parseLength(firstRaw, fontSize));
1001
+ out[property] = setEdge(out[property], secondSide, parseLength(secondRaw, fontSize));
1002
+ }
1003
+ function applyMargin(out, value, fontSize) {
1004
+ const [topRaw, rightRaw = topRaw, bottomRaw = topRaw, leftRaw = rightRaw] = cssValueTokens(value);
1005
+ setMarginEdge(out, "top", topRaw, fontSize);
1006
+ setMarginEdge(out, "right", rightRaw, fontSize);
1007
+ setMarginEdge(out, "bottom", bottomRaw, fontSize);
1008
+ setMarginEdge(out, "left", leftRaw, fontSize);
1009
+ }
1010
+ function applyTwoValueMargin(out, firstSide, secondSide, value, fontSize) {
1011
+ const [firstRaw, secondRaw = firstRaw] = cssValueTokens(value);
1012
+ setMarginEdge(out, firstSide, firstRaw, fontSize);
1013
+ setMarginEdge(out, secondSide, secondRaw, fontSize);
1014
+ }
1015
+ function setMarginEdge(out, side, value, fontSize) {
1016
+ if (value === void 0) return;
1017
+ if (value === "auto" && (side === "left" || side === "right")) {
1018
+ out.margin = setEdge(out.margin, side, 0);
1019
+ if (side === "left") out.marginAutoLeft = true;
1020
+ else out.marginAutoRight = true;
1021
+ return;
1022
+ }
1023
+ const parsed = parseLength(value, fontSize);
1024
+ out.margin = setEdge(out.margin, side, parsed);
1025
+ if (parsed !== void 0) {
1026
+ if (side === "left") out.marginAutoLeft = void 0;
1027
+ if (side === "right") out.marginAutoRight = void 0;
1028
+ }
1029
+ }
1030
+ function parseGridColumn(out, value) {
1031
+ const [startRaw, endRaw] = value.split("/").map((part) => part.trim().toLowerCase());
1032
+ if (startRaw) parseGridColumnLine(out, "gridColumnStart", startRaw);
1033
+ if (endRaw) parseGridColumnLine(out, "gridColumnEnd", endRaw);
1034
+ else if (startRaw?.startsWith("span ")) parseGridColumnLine(out, "gridColumnEnd", startRaw);
1035
+ }
1036
+ function parseGridColumnLine(out, property, value) {
1037
+ const normalized = value.trim().toLowerCase();
1038
+ const span = /^span\s+(\d+)$/.exec(normalized);
1039
+ if (span) {
1040
+ const amount = Number(span[1]);
1041
+ if (Number.isInteger(amount) && amount > 0) out.gridColumnSpan = amount;
1042
+ return;
1043
+ }
1044
+ const line = Number.parseInt(normalized, 10);
1045
+ if (!Number.isInteger(line)) return;
1046
+ out[property] = line;
1047
+ }
1048
+ function parseGridTemplateColumns(value, fontSize) {
1049
+ const expanded = expandGridRepeats(value);
1050
+ const tracks = gridTrackTokens(expanded).map((token) => {
1051
+ if (token === "auto" || token === "min-content" || token === "max-content") return { kind: "fr", value: 1 };
1052
+ const fr = /^([0-9.]+)fr$/.exec(token);
1053
+ if (fr) {
1054
+ const amount = Number(fr[1]);
1055
+ return Number.isFinite(amount) && amount > 0 ? { kind: "fr", value: amount } : void 0;
1056
+ }
1057
+ const percent = parsePercentage(token);
1058
+ if (percent !== void 0) return { kind: "percent", value: percent };
1059
+ const minmax = /^minmax\([^,]+,\s*([^)]+)\)$/.exec(token);
1060
+ if (minmax) return parseGridTemplateColumns(minmax[1], fontSize)?.[0];
1061
+ const length = parseLength(token, fontSize);
1062
+ return length === void 0 ? void 0 : { kind: "length", value: length };
1063
+ });
1064
+ if (tracks.length === 0 || tracks.some((track) => track === void 0)) return void 0;
1065
+ return tracks;
1066
+ }
1067
+ function expandGridRepeats(value) {
1068
+ let out = "";
1069
+ let index = 0;
1070
+ while (index < value.length) {
1071
+ const repeatStart = value.toLowerCase().indexOf("repeat(", index);
1072
+ if (repeatStart === -1) {
1073
+ out += value.slice(index);
1074
+ break;
1075
+ }
1076
+ out += value.slice(index, repeatStart);
1077
+ const argsStart = repeatStart + "repeat(".length;
1078
+ const close = closingParenIndex(value, argsStart);
1079
+ if (close === -1) {
1080
+ out += value.slice(repeatStart);
1081
+ break;
1082
+ }
1083
+ const args = value.slice(argsStart, close);
1084
+ const comma = topLevelCommaIndex(args);
1085
+ const count = Number(comma === -1 ? NaN : args.slice(0, comma).trim());
1086
+ const repeated = comma === -1 ? "" : args.slice(comma + 1).trim();
1087
+ out += Number.isInteger(count) && count > 0 && count <= 24 ? Array.from({ length: count }, () => repeated).join(" ") : "";
1088
+ index = close + 1;
1089
+ }
1090
+ return out;
1091
+ }
1092
+ function gridTrackTokens(value) {
1093
+ return cssValueTokens(value).map((token) => token.toLowerCase());
1094
+ }
1095
+ function cssValueTokens(value) {
1096
+ return cssValueTokensPreservingCase(value).map((token) => token.toLowerCase());
1097
+ }
1098
+ function cssValueTokensPreservingCase(value) {
1099
+ const tokens = [];
1100
+ let token = "";
1101
+ let depth = 0;
1102
+ for (const char of value.trim()) {
1103
+ if (char === "(") depth += 1;
1104
+ if (char === ")") depth = Math.max(0, depth - 1);
1105
+ if (/\s/.test(char) && depth === 0) {
1106
+ if (token) tokens.push(token);
1107
+ token = "";
1108
+ continue;
1109
+ }
1110
+ token += char;
1111
+ }
1112
+ if (token) tokens.push(token);
1113
+ return tokens;
1114
+ }
1115
+ function parseTextDecoration(value) {
1116
+ if (value === "none") return "none";
1117
+ if (value.includes("underline")) return "underline";
1118
+ if (value.includes("line-through")) return "line-through";
1119
+ return void 0;
1120
+ }
1121
+ function parseBackground(out, value) {
1122
+ out.background = parseBackgroundColor(value) ?? out.background;
1123
+ out.backgroundImageUrl = parseBackgroundImageUrl(value) ?? out.backgroundImageUrl;
1124
+ const lower = value.toLowerCase();
1125
+ if (/\bcover\b/.test(lower)) out.backgroundSize = "cover";
1126
+ else if (/\bcontain\b/.test(lower)) out.backgroundSize = "contain";
1127
+ if (/\bno-repeat\b/.test(lower)) out.backgroundRepeat = "no-repeat";
1128
+ else if (/\brepeat-x\b/.test(lower)) out.backgroundRepeat = "repeat-x";
1129
+ else if (/\brepeat-y\b/.test(lower)) out.backgroundRepeat = "repeat-y";
1130
+ else if (/\brepeat\b/.test(lower)) out.backgroundRepeat = "repeat";
1131
+ parseBackgroundPosition(out, lower.replace(/\/\s*(cover|contain|auto)\b/g, ""));
1132
+ }
1133
+ function parseBackgroundColor(value) {
1134
+ const withoutUrls = value.replace(/url\([^)]*\)/gi, " ");
1135
+ const tokens = withoutUrls.match(/#[0-9a-f]{3,8}\b|rgba?\([^)]*\)|hsla?\([^)]*\)|[a-zA-Z]+/g) ?? [];
1136
+ for (const token of tokens) {
1137
+ const color = parseColor(token);
1138
+ if (color) return color;
1139
+ }
1140
+ return void 0;
1141
+ }
1142
+ function parseBackgroundImageUrl(value) {
1143
+ const match = /url\(\s*(?:"([^"]+)"|'([^']+)'|([^)]*?))\s*\)/i.exec(value);
1144
+ return match ? (match[1] ?? match[2] ?? match[3])?.trim() : void 0;
1145
+ }
1146
+ function parseBackgroundPosition(out, value) {
1147
+ const tokens = value.split(/\s+/).map((token) => token.trim()).filter(Boolean).filter((token) => !["url", "repeat", "repeat-x", "repeat-y", "no-repeat", "cover", "contain", "auto"].includes(token));
1148
+ const normalized = tokens.join(" ");
1149
+ if (!normalized) return;
1150
+ if (/\bleft\b/.test(normalized)) out.backgroundPositionX = 0;
1151
+ else if (/\bright\b/.test(normalized)) out.backgroundPositionX = 1;
1152
+ else if (/\bcenter\b/.test(normalized)) out.backgroundPositionX = 0.5;
1153
+ const percentX = /(^|\s)([0-9.]+)%/.exec(normalized);
1154
+ if (percentX) out.backgroundPositionX = Number(percentX[2]) / 100;
1155
+ if (/\btop\b/.test(normalized)) out.backgroundPositionY = 0;
1156
+ else if (/\bbottom\b/.test(normalized)) out.backgroundPositionY = 1;
1157
+ else if (/\bcenter\b/.test(normalized)) out.backgroundPositionY ??= 0.5;
1158
+ const percents = [...normalized.matchAll(/([0-9.]+)%/g)];
1159
+ if (percents[1]) out.backgroundPositionY = Number(percents[1][1]) / 100;
1160
+ }
1161
+ function parseFontFamily(value) {
1162
+ return value.split(",").map((family) => family.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
1163
+ }
1164
+ function parseFontShorthand(out, value, inheritedFontSize) {
1165
+ const tokens = cssValueTokensPreservingCase(value);
1166
+ const sizeIndex = tokens.findIndex((token) => {
1167
+ const [size] = token.split("/");
1168
+ return isFontSizeToken(size) && parseLength(size, inheritedFontSize) !== void 0;
1169
+ });
1170
+ if (sizeIndex < 0) return;
1171
+ for (const token of tokens.slice(0, sizeIndex)) {
1172
+ const normalized = token.toLowerCase();
1173
+ if (normalized === "italic" || normalized === "oblique") out.fontStyle = "italic";
1174
+ else if (normalized === "normal") {
1175
+ out.fontStyle ??= "normal";
1176
+ out.fontWeight ??= "normal";
1177
+ } else if (normalized === "bold" || normalized === "bolder" || normalized === "lighter" || /^[1-9]00$/.test(normalized)) {
1178
+ out.fontWeight = parseFontWeight(normalized);
1179
+ }
1180
+ }
1181
+ const [sizePart, lineHeightPart] = tokens[sizeIndex].split("/");
1182
+ const parsedSize = parseLength(sizePart, inheritedFontSize);
1183
+ if (parsedSize !== void 0) out.fontSize = parsedSize;
1184
+ if (lineHeightPart) out.lineHeight = parseLineHeight(lineHeightPart, parsedSize ?? inheritedFontSize);
1185
+ const family = tokens.slice(sizeIndex + 1).join(" ");
1186
+ if (family.trim()) out.fontFamily = parseFontFamily(family);
1187
+ }
1188
+ function isFontSizeToken(value) {
1189
+ return value !== void 0 && /^-?(?:\d+|\d*\.\d+)(px|pt|em|rem|vw|vh)$/i.test(value.trim());
1190
+ }
1191
+ function parseFontWeight(value) {
1192
+ if (value === "bold" || value === "bolder") return "bold";
1193
+ if (value === "normal" || value === "lighter") return "normal";
1194
+ const numeric = Number(value);
1195
+ if (Number.isFinite(numeric)) return numeric;
1196
+ return "normal";
1197
+ }
1198
+ function parseEdges(value, fontSize) {
1199
+ const lengths = cssValueTokens(value).map((part) => parseLength(part, fontSize));
1200
+ if (lengths.some((length) => length === void 0)) return void 0;
1201
+ const [top, right = top, bottom = top, left = right] = lengths;
1202
+ if (top === right && right === bottom && bottom === left) return top;
1203
+ return { top, right, bottom, left };
1204
+ }
1205
+ function setEdge(edges3, side, value) {
1206
+ if (value === void 0) return edges3;
1207
+ const out = typeof edges3 === "number" ? { top: edges3, right: edges3, bottom: edges3, left: edges3 } : { ...edges3 };
1208
+ if (side === "top" || side === "right" || side === "bottom" || side === "left") out[side] = value;
1209
+ return out;
1210
+ }
1211
+ function parseBorder(out, value, fontSize) {
1212
+ const parsed = parseBorderValue(value, fontSize);
1213
+ out.borderWidth ??= parsed.width;
1214
+ out.borderColor ??= parsed.color;
1215
+ }
1216
+ function parseBorderValue(value, fontSize) {
1217
+ const out = {};
1218
+ if (/\bnone\b/.test(value)) out.width = 0;
1219
+ for (const token of cssValueTokens(value)) {
1220
+ out.width ??= parseLength(token, fontSize);
1221
+ out.color ??= parseColor(token);
1222
+ }
1223
+ return out;
1224
+ }
1225
+ function setBorderSide(out, side, value) {
1226
+ if (side !== "top" && side !== "right" && side !== "bottom" && side !== "left") return;
1227
+ const current = out.borderSides?.[side];
1228
+ const width = value.width ?? current?.width ?? out.borderWidth ?? 0.75;
1229
+ const color = value.color ?? current?.color ?? out.borderColor ?? { r: 0, g: 0, b: 0 };
1230
+ out.borderSides = { ...out.borderSides, [side]: { width, color } };
1231
+ }
1232
+ function borderSideFromProperty(property, suffix) {
1233
+ return property.slice("border-".length, -suffix.length);
1234
+ }
1235
+ function selectorList(prelude) {
1236
+ return (0, import_css_tree.generate)(prelude).split(",").map((selector) => stripWhereSelectors(selector.trim())).filter(Boolean);
1237
+ }
1238
+ function stripWhereSelectors(selector) {
1239
+ let out = "";
1240
+ let index = 0;
1241
+ while (index < selector.length) {
1242
+ const start = selector.indexOf(":where(", index);
1243
+ if (start === -1) {
1244
+ out += selector.slice(index);
1245
+ break;
1246
+ }
1247
+ out += selector.slice(index, start);
1248
+ const argsStart = start + ":where(".length;
1249
+ const close = closingParenIndex(selector, argsStart);
1250
+ if (close === -1) {
1251
+ out += selector.slice(start);
1252
+ break;
1253
+ }
1254
+ out += selector.slice(argsStart, close);
1255
+ index = close + 1;
1256
+ }
1257
+ return out;
1258
+ }
1259
+ function matchesSelector(node, selector) {
1260
+ const parts = selectorParts(selector);
1261
+ if (parts.length === 0) return false;
1262
+ const last = parts[parts.length - 1];
1263
+ if (!matchesCompound(node, last.selector)) return false;
1264
+ last.node = node;
1265
+ let cursor = node.parent;
1266
+ for (let i = parts.length - 2; i >= 0; i -= 1) {
1267
+ const part = parts[i];
1268
+ const combinator = parts[i + 1].combinator;
1269
+ if (combinator === ">") {
1270
+ if (!cursor || !matchesCompound(cursor, part.selector)) return false;
1271
+ cursor = cursor.parent;
1272
+ continue;
1273
+ }
1274
+ if (combinator === "+") {
1275
+ const previous = previousElement(parts[i + 1].node ?? node);
1276
+ if (!previous || !matchesCompound(previous, part.selector)) return false;
1277
+ cursor = previous.parent;
1278
+ parts[i].node = previous;
1279
+ continue;
1280
+ }
1281
+ if (combinator === "~") {
1282
+ let sibling = previousElement(parts[i + 1].node ?? node);
1283
+ while (sibling && !matchesCompound(sibling, part.selector)) sibling = previousElement(sibling);
1284
+ if (!sibling) return false;
1285
+ cursor = sibling.parent;
1286
+ parts[i].node = sibling;
1287
+ continue;
1288
+ }
1289
+ while (cursor && !matchesCompound(cursor, part.selector)) cursor = cursor.parent;
1290
+ if (!cursor) return false;
1291
+ parts[i].node = cursor;
1292
+ cursor = cursor.parent;
1293
+ }
1294
+ return true;
1295
+ }
1296
+ function selectorParts(selector) {
1297
+ const parts = [];
1298
+ let buffer = "";
1299
+ let combinator = " ";
1300
+ let depth = 0;
1301
+ let quote = "";
1302
+ const flush = () => {
1303
+ const trimmed = buffer.trim();
1304
+ if (trimmed) parts.push({ selector: trimmed, combinator });
1305
+ buffer = "";
1306
+ combinator = " ";
1307
+ };
1308
+ for (let i = 0; i < selector.length; i += 1) {
1309
+ const char = selector[i];
1310
+ if (quote) {
1311
+ buffer += char;
1312
+ if (char === quote) quote = "";
1313
+ continue;
1314
+ }
1315
+ if (char === '"' || char === "'") {
1316
+ quote = char;
1317
+ buffer += char;
1318
+ continue;
1319
+ }
1320
+ if (char === "[" || char === "(") depth += 1;
1321
+ if (char === "]" || char === ")") depth = Math.max(0, depth - 1);
1322
+ if (depth === 0 && (char === ">" || char === "+" || char === "~")) {
1323
+ flush();
1324
+ combinator = char;
1325
+ continue;
1326
+ }
1327
+ if (depth === 0 && /\s/.test(char)) {
1328
+ let next = i + 1;
1329
+ while (next < selector.length && /\s/.test(selector[next])) next += 1;
1330
+ if (!buffer.trim() || selector[next] === ">" || selector[next] === "+" || selector[next] === "~") {
1331
+ i = next - 1;
1332
+ continue;
1333
+ }
1334
+ flush();
1335
+ i = next - 1;
1336
+ continue;
1337
+ }
1338
+ buffer += char;
1339
+ }
1340
+ flush();
1341
+ return parts;
1342
+ }
1343
+ function matchesCompound(node, selector) {
1344
+ const stripped = stripSupportedPseudos(selector, node);
1345
+ if (stripped === void 0) return false;
1346
+ selector = stripped;
1347
+ if (selector === "*") return true;
1348
+ if (!matchesAttributes(node, stripSimpleSelectors(selector))) return false;
1349
+ const id = simpleSelectorValues(selector, "#")[0];
1350
+ if (id && node.attrs.id !== id) return false;
1351
+ const classes = simpleSelectorValues(selector, ".");
1352
+ const nodeClasses = new Set((node.attrs.class ?? "").split(/\s+/).filter(Boolean));
1353
+ if (classes.some((klass) => !nodeClasses.has(klass))) return false;
1354
+ const tag = stripSimpleSelectors(selector).replace(/\[[^\]]+\]/g, "").trim();
1355
+ return tag.length === 0 || tag === "*" || tag.toLowerCase() === node.tag;
1356
+ }
1357
+ function specificity(selector) {
1358
+ const ids = selectorParts(selector).reduce((sum, part) => sum + simpleSelectorValues(part.selector, "#").length, 0);
1359
+ const classes = selectorParts(selector).reduce((sum, part) => sum + simpleSelectorValues(part.selector, ".").length, 0) + (selector.match(/\[[^\]]+\]/g) ?? []).length + pseudoSelectors(selector).length;
1360
+ const tags = selectorParts(selector).filter((part) => /^[A-Za-z]/.test(part.selector.replace(/[#.:\[].*$/, ""))).length;
1361
+ return ids * 100 + classes * 10 + tags;
1362
+ }
1363
+ function stripSupportedPseudos(selector, node) {
1364
+ let out = selector;
1365
+ const pseudos = pseudoSelectors(out).reverse();
1366
+ for (const match of pseudos) {
1367
+ const name = match.name;
1368
+ const arg = match.arg?.trim();
1369
+ if (name === "first-child") {
1370
+ if (elementIndex(node) !== 1) return void 0;
1371
+ } else if (name === "not") {
1372
+ if (arg === ":last-child" && elementIndex(node) === elementSiblings(node).length) return void 0;
1373
+ else if (arg === ":first-child" && elementIndex(node) === 1) return void 0;
1374
+ else if (arg !== ":last-child" && arg !== ":first-child") return void 0;
1375
+ } else if (name === "root") {
1376
+ if (node.parent !== void 0) return void 0;
1377
+ } else if (name === "last-child") {
1378
+ if (elementIndex(node) !== elementSiblings(node).length) return void 0;
1379
+ } else if (name === "nth-child") {
1380
+ if (!matchesNth(elementIndex(node), arg)) return void 0;
1381
+ } else if (name === "first-of-type") {
1382
+ if (typeIndex(node) !== 1) return void 0;
1383
+ } else if (name === "last-of-type") {
1384
+ if (typeIndex(node) !== typeSiblings(node).length) return void 0;
1385
+ } else if (name === "nth-of-type") {
1386
+ if (!matchesNth(typeIndex(node), arg)) return void 0;
1387
+ } else {
1388
+ return void 0;
1389
+ }
1390
+ out = out.slice(0, match.start) + out.slice(match.end);
1391
+ }
1392
+ return out;
1393
+ }
1394
+ function simpleSelectorValues(selector, prefix) {
1395
+ const values = [];
1396
+ for (let index = 0; index < selector.length; index += 1) {
1397
+ if (selector[index] !== prefix || isEscaped(selector, index)) continue;
1398
+ const read = readCssIdentifier(selector, index + 1);
1399
+ if (read.value) values.push(read.value);
1400
+ index = read.end - 1;
1401
+ }
1402
+ return values;
1403
+ }
1404
+ function stripSimpleSelectors(selector) {
1405
+ let out = "";
1406
+ for (let index = 0; index < selector.length; index += 1) {
1407
+ const char = selector[index];
1408
+ if ((char === "." || char === "#") && !isEscaped(selector, index)) {
1409
+ index = readCssIdentifier(selector, index + 1).end - 1;
1410
+ continue;
1411
+ }
1412
+ out += char;
1413
+ }
1414
+ return out;
1415
+ }
1416
+ function readCssIdentifier(input, start) {
1417
+ let value = "";
1418
+ let index = start;
1419
+ while (index < input.length) {
1420
+ const char = input[index];
1421
+ if (char === "\\") {
1422
+ const escaped = readCssEscape(input, index);
1423
+ value += escaped.value;
1424
+ index = escaped.end;
1425
+ continue;
1426
+ }
1427
+ if (char === "[") {
1428
+ const close = input.indexOf("]", index + 1);
1429
+ if (close === -1) break;
1430
+ const body = input.slice(index + 1, close);
1431
+ if (body.includes("=")) break;
1432
+ value += input.slice(index, close + 1);
1433
+ index = close + 1;
1434
+ continue;
1435
+ }
1436
+ if (/[\s>+~.#(]/.test(char)) break;
1437
+ value += char;
1438
+ index += 1;
1439
+ }
1440
+ return { value, end: index };
1441
+ }
1442
+ function readCssEscape(input, start) {
1443
+ let index = start + 1;
1444
+ const hex = /^[0-9a-fA-F]{1,6}/.exec(input.slice(index))?.[0];
1445
+ if (hex) {
1446
+ index += hex.length;
1447
+ if (/\s/.test(input[index] ?? "")) index += 1;
1448
+ return { value: String.fromCodePoint(Number.parseInt(hex, 16)), end: index };
1449
+ }
1450
+ if (index >= input.length) return { value: "", end: index };
1451
+ return { value: input[index], end: index + 1 };
1452
+ }
1453
+ function isEscaped(input, index) {
1454
+ let slashCount = 0;
1455
+ for (let cursor = index - 1; cursor >= 0 && input[cursor] === "\\"; cursor -= 1) slashCount += 1;
1456
+ return slashCount % 2 === 1;
1457
+ }
1458
+ function pseudoSelectors(selector) {
1459
+ const pseudos = [];
1460
+ for (let index = 0; index < selector.length; index += 1) {
1461
+ if (selector[index] !== ":" || isEscaped(selector, index)) continue;
1462
+ if (isInsideClassSelector(selector, index)) continue;
1463
+ if (selector[index + 1] === ":") {
1464
+ const read2 = readCssIdentifier(selector, index + 2);
1465
+ if (read2.value) pseudos.push({ name: `:${read2.value}`, start: index, end: read2.end });
1466
+ index = read2.end - 1;
1467
+ continue;
1468
+ }
1469
+ const read = readCssIdentifier(selector, index + 1);
1470
+ if (!read.value) continue;
1471
+ let end = read.end;
1472
+ let arg;
1473
+ if (selector[end] === "(") {
1474
+ const close = closingParenIndex(selector, end + 1);
1475
+ if (close === -1) return pseudos;
1476
+ arg = selector.slice(end + 1, close);
1477
+ end = close + 1;
1478
+ }
1479
+ pseudos.push({ name: read.value, arg, start: index, end });
1480
+ index = end - 1;
1481
+ }
1482
+ return pseudos;
1483
+ }
1484
+ function isInsideClassSelector(selector, index) {
1485
+ for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
1486
+ const char = selector[cursor];
1487
+ if (isEscaped(selector, cursor)) continue;
1488
+ if (char === ".") return true;
1489
+ if (/[\s>+~#\[]/.test(char)) return false;
1490
+ }
1491
+ return false;
1492
+ }
1493
+ function matchesAttributes(node, selector) {
1494
+ for (const match of selector.matchAll(/\[([^\]=~|^$*\s]+)(?:\s*([~|^$*]?=)\s*(?:"([^"]*)"|'([^']*)'|([^\]\s]+)))?\]/g)) {
1495
+ const name = match[1];
1496
+ const op = match[2];
1497
+ const expected = match[3] ?? match[4] ?? match[5] ?? "";
1498
+ const actual = node.attrs[name];
1499
+ if (actual === void 0) return false;
1500
+ if (!op) continue;
1501
+ if (op === "=" && actual !== expected) return false;
1502
+ if (op === "~=" && !actual.split(/\s+/).includes(expected)) return false;
1503
+ if (op === "|=" && actual !== expected && !actual.startsWith(`${expected}-`)) return false;
1504
+ if (op === "^=" && !actual.startsWith(expected)) return false;
1505
+ if (op === "$=" && !actual.endsWith(expected)) return false;
1506
+ if (op === "*=" && !actual.includes(expected)) return false;
1507
+ }
1508
+ return true;
1509
+ }
1510
+ function previousElement(node) {
1511
+ const siblings = elementSiblings(node);
1512
+ return siblings[elementIndex(node) - 2];
1513
+ }
1514
+ function elementSiblings(node) {
1515
+ return node.parent?.children.filter((child) => child.kind === "element") ?? [];
1516
+ }
1517
+ function typeSiblings(node) {
1518
+ return elementSiblings(node).filter((sibling) => sibling.tag === node.tag);
1519
+ }
1520
+ function elementIndex(node) {
1521
+ return elementSiblings(node).indexOf(node) + 1;
1522
+ }
1523
+ function typeIndex(node) {
1524
+ return typeSiblings(node).indexOf(node) + 1;
1525
+ }
1526
+ function matchesNth(index, arg) {
1527
+ if (!arg) return false;
1528
+ const normalized = arg.replace(/\s+/g, "").toLowerCase();
1529
+ if (normalized === "odd") return index % 2 === 1;
1530
+ if (normalized === "even") return index % 2 === 0;
1531
+ const exact = Number(normalized);
1532
+ if (Number.isInteger(exact)) return index === exact;
1533
+ const match = /^([+-]?\d*)n([+-]\d+)?$/.exec(normalized);
1534
+ if (!match) return false;
1535
+ const aRaw = match[1];
1536
+ const a = aRaw === "" || aRaw === "+" ? 1 : aRaw === "-" ? -1 : Number(aRaw);
1537
+ const b = match[2] ? Number(match[2]) : 0;
1538
+ if (!Number.isFinite(a) || !Number.isFinite(b) || a === 0) return index === b;
1539
+ return (index - b) / a >= 0 && Number.isInteger((index - b) / a);
1540
+ }
1541
+ function compareRule(a, b) {
1542
+ return a.specificity - b.specificity || a.order - b.order;
1543
+ }
1544
+
1545
+ // src/dom.ts
1546
+ var import_parse5 = require("parse5");
1547
+ function parseHtml(html) {
1548
+ const fragment = (0, import_parse5.parseFragment)(html);
1549
+ const root = {
1550
+ kind: "element",
1551
+ tag: "body",
1552
+ attrs: {},
1553
+ children: []
1554
+ };
1555
+ const stylesheets = [];
1556
+ root.children = fragment.childNodes.flatMap((child) => convertNode(child, root, stylesheets));
1557
+ return { root, stylesheets };
1558
+ }
1559
+ function convertNode(node, parent, stylesheets) {
1560
+ if (isText(node)) {
1561
+ return [{ kind: "text", value: node.value, parent }];
1562
+ }
1563
+ if (!isElement(node)) return [];
1564
+ const tag = node.tagName.toLowerCase();
1565
+ const attrs = Object.fromEntries(node.attrs.map((attr) => [attr.name, attr.value]));
1566
+ const element = { kind: "element", tag, attrs, children: [], parent };
1567
+ element.children = node.childNodes.flatMap((child) => convertNode(child, element, stylesheets));
1568
+ if (tag === "style") {
1569
+ const css = element.children.filter((child) => child.kind === "text").map((child) => child.value).join("");
1570
+ if (css.trim()) stylesheets.push(css);
1571
+ return [];
1572
+ }
1573
+ if (tag === "script" || tag === "noscript" || tag === "template") return [];
1574
+ return [element];
1575
+ }
1576
+ function isText(node) {
1577
+ return node.nodeName === "#text";
1578
+ }
1579
+ function isElement(node) {
1580
+ return "tagName" in node && Array.isArray(node.childNodes);
1581
+ }
1582
+
1583
+ // src/render.ts
1584
+ var import_boxpdf = require("boxpdf");
1585
+ function renderStyledTree(root, options) {
1586
+ const warnings = [];
1587
+ const nodes = root.children.flatMap((child) => renderNode(child, options, warnings));
1588
+ return { nodes, warnings };
1589
+ }
1590
+ function renderNode(node, options, warnings) {
1591
+ if ("text" in node) {
1592
+ const trimmed = node.text.trim();
1593
+ return trimmed ? [(0, import_boxpdf.text)(trimmed, textOptions(node, options))] : [];
1594
+ }
1595
+ if (node.style.display === "none") return [];
1596
+ if (node.style.display === "contents") return node.children.flatMap((child) => renderNode(child, options, warnings));
1597
+ if (node.node.tag === "img") {
1598
+ const rendered = renderImageForLayout(node, options, warnings);
1599
+ return rendered ? [rendered] : [];
1600
+ }
1601
+ if (node.node.tag === "br") return [(0, import_boxpdf.text)("", textOptions({ style: node.style }, options))];
1602
+ if (node.node.tag === "hr") return [(0, import_boxpdf.hline)({ color: node.style.borderColor ?? { r: 0, g: 0, b: 0 }, thickness: node.style.borderWidth ?? 1 })];
1603
+ if (node.node.tag === "ul" || node.node.tag === "ol") return [renderList(node, options, warnings)];
1604
+ if (node.node.tag === "table") return renderTable(node, options, warnings);
1605
+ if (node.style.display === "grid" || node.style.display === "inline-grid") return [renderGrid(node, options, warnings)];
1606
+ if (node.style.display === "flex" || node.style.display === "inline-flex") return [renderFlex(node, options, warnings)];
1607
+ if (isInlineContainer(node)) {
1608
+ return renderInlineGroup(node, options, warnings);
1609
+ }
1610
+ return [renderBlock(node, options, warnings)];
1611
+ }
1612
+ function renderBlock(node, options, warnings, stretch = node.style.display === "block") {
1613
+ const children = renderBlockChildren(node, options, warnings);
1614
+ return (0, import_boxpdf.vstack)(
1615
+ {
1616
+ width: cssBoxWidth(node),
1617
+ height: cssBoxHeight(node),
1618
+ margin: node.style.margin,
1619
+ padding: layoutPadding(node),
1620
+ gap: node.style.gap ?? 0,
1621
+ background: node.style.background,
1622
+ backgroundImage: backgroundImage(node, options),
1623
+ border: border(node),
1624
+ borderSides: node.style.borderSides,
1625
+ borderRadius: node.style.borderRadius,
1626
+ overflow: node.style.overflow,
1627
+ position: node.style.position,
1628
+ top: node.style.top,
1629
+ right: node.style.right,
1630
+ bottom: node.style.bottom,
1631
+ left: node.style.left,
1632
+ zIndex: node.style.zIndex,
1633
+ align: stretch ? "stretch" : "start"
1634
+ },
1635
+ ...children
1636
+ );
1637
+ }
1638
+ function renderBlockChildren(node, options, warnings) {
1639
+ const out = [];
1640
+ let inlineBuffer = [];
1641
+ let floats = [];
1642
+ const flushInline = () => {
1643
+ const runs = collectInlineRuns(inlineBuffer, options, warnings);
1644
+ if (runs.length > 0 || floats.length > 0) {
1645
+ out.push((0, import_boxpdf.paragraph)({ width: contentWidth(node), align: node.style.textAlign, wrap: shouldWrap(node.style), floats, textIndent: node.style.textIndent }, ...runs));
1646
+ floats = [];
1647
+ }
1648
+ inlineBuffer = [];
1649
+ };
1650
+ for (const child of flattenDisplayContents(node.children)) {
1651
+ if (!("text" in child) && child.style.float && child.style.float !== "none") {
1652
+ floats.push({ node: renderFloatNode(child, options, warnings), side: child.style.float });
1653
+ continue;
1654
+ }
1655
+ if (isInlineLike(child)) {
1656
+ inlineBuffer.push(child);
1657
+ continue;
1658
+ }
1659
+ if (inlineBuffer.some(hasInlineContent)) flushInline();
1660
+ else inlineBuffer = [];
1661
+ const rendered = renderNode(child, options, warnings);
1662
+ if (floats.length > 0) {
1663
+ const attached = attachFloatsToFirstParagraph(rendered, floats);
1664
+ if (attached.attached) {
1665
+ out.push(...attached.nodes);
1666
+ floats = [];
1667
+ continue;
1668
+ }
1669
+ flushInline();
1670
+ }
1671
+ out.push(...rendered);
1672
+ }
1673
+ flushInline();
1674
+ return out;
1675
+ }
1676
+ function hasInlineContent(node) {
1677
+ if ("text" in node) return node.text.trim().length > 0;
1678
+ if (node.node.tag === "br") return true;
1679
+ return node.children.some(hasInlineContent);
1680
+ }
1681
+ function flattenDisplayContents(nodes) {
1682
+ return nodes.flatMap((node) => {
1683
+ if ("text" in node || node.style.display !== "contents") return [node];
1684
+ return flattenDisplayContents(node.children);
1685
+ });
1686
+ }
1687
+ function renderFloatNode(node, options, warnings) {
1688
+ if (node.node.tag === "table") return renderTable(node, options, warnings)[0] ?? renderBlock(node, options, warnings);
1689
+ if (node.style.display === "flex" || node.style.display === "inline-flex") return renderFlex(node, options, warnings);
1690
+ if (node.style.display === "grid" || node.style.display === "inline-grid") return renderGrid(node, options, warnings);
1691
+ return renderBlock(node, options, warnings);
1692
+ }
1693
+ function attachFloatsToFirstParagraph(nodes, floats) {
1694
+ let attached = false;
1695
+ const next = nodes.map((node) => {
1696
+ if (attached) return node;
1697
+ const result = attachFloatsToNode(node, floats);
1698
+ attached = result.attached;
1699
+ return result.node;
1700
+ });
1701
+ return { nodes: next, attached };
1702
+ }
1703
+ function attachFloatsToNode(node, floats) {
1704
+ if (node.kind === "paragraph") {
1705
+ return { node: { ...node, props: { ...node.props, floats: [...floats, ...node.props.floats ?? []] } }, attached: true };
1706
+ }
1707
+ if (node.kind !== "vstack" && node.kind !== "hstack") return { node, attached: false };
1708
+ let attached = false;
1709
+ const children = node.children.map((child) => {
1710
+ if (attached) return child;
1711
+ const result = attachFloatsToNode(child, floats);
1712
+ attached = result.attached;
1713
+ return result.node;
1714
+ });
1715
+ return attached ? { node: { ...node, children }, attached } : { node, attached };
1716
+ }
1717
+ function renderFlex(node, options, warnings) {
1718
+ const ordered = orderedChildren(node.children);
1719
+ const sourceChildren = node.style.flexDirection.endsWith("-reverse") ? [...ordered].reverse() : ordered;
1720
+ const width = cssBoxWidth(node);
1721
+ const justifyWidth = contentWidth(node);
1722
+ const children = flexChildrenWithJustification(
1723
+ sourceChildren.flatMap((child) => renderFlexChild(child, options, warnings)).map(defaultFlexItem),
1724
+ node.style.justifyContent,
1725
+ node.style.flexDirection,
1726
+ justifyWidth,
1727
+ node.style.gap ?? 0
1728
+ );
1729
+ const style = {
1730
+ width,
1731
+ height: cssBoxHeight(node),
1732
+ margin: node.style.margin,
1733
+ padding: layoutPadding(node),
1734
+ gap: node.style.gap ?? 0,
1735
+ align: node.style.alignItems,
1736
+ justify: children.justify,
1737
+ background: node.style.background,
1738
+ backgroundImage: backgroundImage(node, options),
1739
+ border: border(node),
1740
+ borderSides: node.style.borderSides,
1741
+ borderRadius: node.style.borderRadius,
1742
+ overflow: node.style.overflow,
1743
+ position: node.style.position,
1744
+ top: node.style.top,
1745
+ right: node.style.right,
1746
+ bottom: node.style.bottom,
1747
+ left: node.style.left,
1748
+ zIndex: node.style.zIndex
1749
+ };
1750
+ return node.style.flexDirection.startsWith("row") ? (0, import_boxpdf.hstack)(style, ...children.nodes) : (0, import_boxpdf.vstack)(style, ...children.nodes);
1751
+ }
1752
+ function flexChildrenWithJustification(children, justify, direction, width, gap) {
1753
+ if (!direction.startsWith("row") || justify !== "between" || children.length < 2) return { nodes: children, justify };
1754
+ const available = width ?? Number.POSITIVE_INFINITY;
1755
+ if (!Number.isFinite(available)) return { nodes: children, justify };
1756
+ const measuredWidths = children.map((child) => (0, import_boxpdf.measure)(child, Number.POSITIVE_INFINITY).width);
1757
+ const childWidth = measuredWidths.reduce((sum, width2) => sum + width2, 0);
1758
+ const spacerWidth = Math.max(0, (available - childWidth - gap * (children.length - 1)) / (children.length - 1));
1759
+ const nodes = children.map((child, index) => setFlexItemWidth(child, measuredWidths[index] + (index === children.length - 1 ? 0 : spacerWidth)));
1760
+ return { nodes, justify: "start" };
1761
+ }
1762
+ function setFlexItemWidth(node, width) {
1763
+ if (node.kind === "vstack" || node.kind === "hstack") {
1764
+ return { ...node, style: { ...node.style, width, shrink: 0 } };
1765
+ }
1766
+ if (node.kind === "text") {
1767
+ return { ...node, props: { ...node.props, width, shrink: 0 } };
1768
+ }
1769
+ return node;
1770
+ }
1771
+ function renderFlexChild(node, options, warnings) {
1772
+ if (!("text" in node) && node.node.tag === "img") {
1773
+ const rendered = renderImageForLayout(node, options, warnings);
1774
+ return rendered ? [rendered] : [];
1775
+ }
1776
+ if (!("text" in node) && isInlineContainer(node)) return [renderBlock(node, options, warnings)];
1777
+ if (!("text" in node) && node.style.display === "block") return [renderBlock(node, options, warnings, false)];
1778
+ return renderNode(node, options, warnings);
1779
+ }
1780
+ function renderGrid(node, options, warnings) {
1781
+ const gridGap = node.style.columnGap ?? node.style.gap ?? 0;
1782
+ const gridWidth = contentWidth(node) ?? (node.style.display === "inline-grid" ? inlineGridIntrinsicWidth(node.children.filter(hasRenderedContent).flatMap((child) => renderNode(child, options, warnings)), node.style.gridTemplateColumns, gridGap, options.width) : options.width);
1783
+ const tracks = resolveGridTracks(node.style.gridTemplateColumns, gridWidth, gridGap);
1784
+ if (tracks.length === 0) return renderBlock(node, options, warnings);
1785
+ const rows = gridRows(node, tracks, options, warnings);
1786
+ return (0, import_boxpdf.vstack)(
1787
+ {
1788
+ width: cssBoxWidth(node),
1789
+ height: cssBoxHeight(node),
1790
+ margin: node.style.margin,
1791
+ padding: layoutPadding(node),
1792
+ gap: node.style.rowGap ?? node.style.gap ?? 0,
1793
+ background: node.style.background,
1794
+ backgroundImage: backgroundImage(node, options),
1795
+ border: border(node),
1796
+ borderSides: node.style.borderSides,
1797
+ borderRadius: node.style.borderRadius,
1798
+ overflow: node.style.overflow,
1799
+ position: node.style.position,
1800
+ top: node.style.top,
1801
+ right: node.style.right,
1802
+ bottom: node.style.bottom,
1803
+ left: node.style.left,
1804
+ zIndex: node.style.zIndex
1805
+ },
1806
+ ...rows
1807
+ );
1808
+ }
1809
+ function gridRows(node, tracks, options, warnings) {
1810
+ const rows = [];
1811
+ let row = [];
1812
+ let cursor = 0;
1813
+ const gap = node.style.columnGap ?? node.style.gap ?? 0;
1814
+ for (const child of orderedChildren(node.children).filter(hasRenderedContent)) {
1815
+ const rendered = renderNode(child, options, warnings);
1816
+ if (rendered.length === 0) continue;
1817
+ const requestedStart = !("text" in child) ? child.style.gridColumnStart : void 0;
1818
+ let column = requestedStart !== void 0 ? Math.max(0, requestedStart - 1) : cursor;
1819
+ let span = gridColumnSpan(child, tracks.length, column);
1820
+ if (column >= tracks.length) column = tracks.length - 1;
1821
+ span = Math.max(1, Math.min(span, tracks.length - column));
1822
+ if (column < cursor || column + span > tracks.length) {
1823
+ rows.push(row);
1824
+ row = [];
1825
+ cursor = 0;
1826
+ column = requestedStart !== void 0 ? Math.max(0, Math.min(requestedStart - 1, tracks.length - 1)) : 0;
1827
+ span = Math.max(1, Math.min(gridColumnSpan(child, tracks.length, column), tracks.length - column));
1828
+ }
1829
+ row.push({ column, span, node: gridCellNode(rendered, gridSpanWidth(tracks, column, span, gap)) });
1830
+ cursor = column + span;
1831
+ if (cursor >= tracks.length) {
1832
+ rows.push(row);
1833
+ row = [];
1834
+ cursor = 0;
1835
+ }
1836
+ }
1837
+ if (row.length > 0) rows.push(row);
1838
+ return rows.map((cells) => renderGridRow(cells, tracks, gap, node.style.alignItems));
1839
+ }
1840
+ function gridColumnSpan(child, trackCount, column) {
1841
+ if ("text" in child) return 1;
1842
+ if (child.style.gridColumnSpan !== void 0) return child.style.gridColumnSpan;
1843
+ if (child.style.gridColumnStart !== void 0 && child.style.gridColumnEnd !== void 0) {
1844
+ return Math.max(1, child.style.gridColumnEnd - child.style.gridColumnStart);
1845
+ }
1846
+ return Math.max(1, Math.min(1, trackCount - column));
1847
+ }
1848
+ function gridSpanWidth(tracks, column, span, gap) {
1849
+ return tracks.slice(column, column + span).reduce((sum, width) => sum + width, 0) + gap * Math.max(0, span - 1);
1850
+ }
1851
+ function gridCellNode(nodes, width) {
1852
+ const node = nodes.length === 1 ? nodes[0] : (0, import_boxpdf.vstack)({}, ...nodes);
1853
+ return constrainGridItem(node, width);
1854
+ }
1855
+ function renderGridRow(cells, tracks, gap, align) {
1856
+ const rowChildren = [];
1857
+ let cursor = 0;
1858
+ for (const cell of cells.sort((a, b) => a.column - b.column)) {
1859
+ while (cursor < cell.column) {
1860
+ rowChildren.push((0, import_boxpdf.vstack)({ width: tracks[cursor] ?? 0 }));
1861
+ cursor += 1;
1862
+ }
1863
+ rowChildren.push(cell.node);
1864
+ cursor = cell.column + cell.span;
1865
+ }
1866
+ while (cursor < tracks.length) {
1867
+ rowChildren.push((0, import_boxpdf.vstack)({ width: tracks[cursor] ?? 0 }));
1868
+ cursor += 1;
1869
+ }
1870
+ return (0, import_boxpdf.hstack)({ gap, align }, ...stretchGridRow(rowChildren, tracksForCells(cells, tracks, gap, rowChildren.length)));
1871
+ }
1872
+ function tracksForCells(cells, tracks, gap, childCount) {
1873
+ if (cells.every((cell) => cell.span === 1) && childCount === tracks.length) return tracks;
1874
+ const out = [];
1875
+ let cursor = 0;
1876
+ for (const cell of cells.sort((a, b) => a.column - b.column)) {
1877
+ while (cursor < cell.column) {
1878
+ out.push(tracks[cursor] ?? 0);
1879
+ cursor += 1;
1880
+ }
1881
+ out.push(gridSpanWidth(tracks, cell.column, cell.span, gap));
1882
+ cursor = cell.column + cell.span;
1883
+ }
1884
+ while (cursor < tracks.length) {
1885
+ out.push(tracks[cursor] ?? 0);
1886
+ cursor += 1;
1887
+ }
1888
+ return out;
1889
+ }
1890
+ function inlineGridIntrinsicWidth(children, tracks, gap, parentWidth) {
1891
+ if (!tracks || tracks.length === 0) {
1892
+ const widths2 = children.map((child) => (0, import_boxpdf.measure)(child, parentWidth ?? Number.POSITIVE_INFINITY).width);
1893
+ return widths2.length === 0 ? void 0 : Math.max(0, ...widths2);
1894
+ }
1895
+ const widths = tracks.map((track, trackIndex) => {
1896
+ if (track.kind === "length") return track.value;
1897
+ return children.filter((_, childIndex) => childIndex % tracks.length === trackIndex).reduce((max, child) => Math.max(max, (0, import_boxpdf.measure)(child, parentWidth ?? Number.POSITIVE_INFINITY).width), 0);
1898
+ });
1899
+ return widths.reduce((sum, width) => sum + width, 0) + gap * Math.max(0, widths.length - 1);
1900
+ }
1901
+ function hasRenderedContent(node) {
1902
+ if ("text" in node) return node.text.trim().length > 0;
1903
+ return node.style.display !== "none";
1904
+ }
1905
+ function orderedChildren(nodes) {
1906
+ return nodes.map((node, index) => ({ node, index })).sort((a, b) => orderFor(a.node) - orderFor(b.node) || a.index - b.index).map(({ node }) => node);
1907
+ }
1908
+ function orderFor(node) {
1909
+ return "text" in node ? 0 : node.style.order ?? 0;
1910
+ }
1911
+ function resolveGridTracks(tracks, width, gap) {
1912
+ if (!tracks || tracks.length === 0) {
1913
+ return width === void 0 ? [] : [width];
1914
+ }
1915
+ const available = width === void 0 ? void 0 : Math.max(0, width - gap * Math.max(0, tracks.length - 1));
1916
+ const fixed = tracks.reduce((sum, track) => {
1917
+ if (track.kind === "length") return sum + track.value;
1918
+ if (track.kind === "percent" && available !== void 0) return sum + available * track.value;
1919
+ return sum;
1920
+ }, 0);
1921
+ const fr = tracks.reduce((sum, track) => sum + (track.kind === "fr" ? track.value : 0), 0);
1922
+ const frUnit = available === void 0 || fr === 0 ? 0 : Math.max(0, available - fixed) / fr;
1923
+ return tracks.map((track) => {
1924
+ if (track.kind === "length") return track.value;
1925
+ if (track.kind === "percent" && available !== void 0) return available * track.value;
1926
+ return frUnit * track.value;
1927
+ });
1928
+ }
1929
+ function constrainGridItem(node, width) {
1930
+ if (node.kind === "vstack" || node.kind === "hstack") {
1931
+ return constrainBlockDescendants({ ...node, style: { ...node.style, width: node.style.width ?? width, shrink: node.style.shrink ?? 1 } }, width);
1932
+ }
1933
+ if (node.kind === "paragraph") {
1934
+ return { ...node, props: { ...node.props, width: node.props.width ?? width } };
1935
+ }
1936
+ if (node.kind === "text") {
1937
+ return { ...node, props: { ...node.props, width: node.props.width ?? width, shrink: node.props.shrink ?? 1 } };
1938
+ }
1939
+ return (0, import_boxpdf.vstack)({ width, shrink: 1 }, node);
1940
+ }
1941
+ function constrainBlockDescendants(node, width) {
1942
+ const innerWidth = boxNodeContentWidth(node, width);
1943
+ const children = node.children.map((child) => constrainFlowChild(child, innerWidth));
1944
+ return { ...node, children };
1945
+ }
1946
+ function constrainFlowChild(child, width) {
1947
+ if (child.kind === "paragraph") {
1948
+ return { ...child, props: { ...child.props, width: child.props.width === void 0 ? width : Math.min(child.props.width, width) } };
1949
+ }
1950
+ if (child.kind === "text") {
1951
+ return { ...child, props: { ...child.props, width: child.props.width === void 0 ? width : Math.min(child.props.width, width) } };
1952
+ }
1953
+ if (child.kind === "vstack" || child.kind === "hstack") {
1954
+ const nextWidth = child.style.width === void 0 ? width : Math.min(child.style.width, width);
1955
+ return constrainBlockDescendants({ ...child, style: { ...child.style, width: nextWidth } }, nextWidth);
1956
+ }
1957
+ return child;
1958
+ }
1959
+ function boxNodeContentWidth(node, width) {
1960
+ const padding = edges(node.style.padding);
1961
+ const borderWidth = node.style.border?.width ?? 0;
1962
+ const leftBorder = node.style.borderSides?.left?.width ?? borderWidth;
1963
+ const rightBorder = node.style.borderSides?.right?.width ?? borderWidth;
1964
+ return Math.max(0, width - padding.left - padding.right - leftBorder - rightBorder);
1965
+ }
1966
+ function stretchGridRow(nodes, tracks) {
1967
+ const heights = nodes.map((node, index) => (0, import_boxpdf.measure)(node, tracks[index] ?? tracks[tracks.length - 1] ?? Number.POSITIVE_INFINITY).height);
1968
+ const rowHeight = Math.max(0, ...heights);
1969
+ return nodes.map((node, index) => {
1970
+ if (node.kind === "vstack" || node.kind === "hstack") {
1971
+ return { ...node, style: { ...node.style, height: node.style.height ?? rowHeight } };
1972
+ }
1973
+ return (0, import_boxpdf.vstack)({ width: tracks[index], height: rowHeight }, node);
1974
+ });
1975
+ }
1976
+ function defaultFlexItem(node) {
1977
+ if (node.kind === "vstack" || node.kind === "hstack") {
1978
+ return { ...node, style: { ...node.style, shrink: node.style.shrink ?? 1 } };
1979
+ }
1980
+ if (node.kind === "text") {
1981
+ return { ...node, props: { ...node.props, shrink: node.props.shrink ?? 1 } };
1982
+ }
1983
+ return node;
1984
+ }
1985
+ function renderInlineGroup(node, options, warnings) {
1986
+ const runs = collectInlineRuns([node], options, warnings);
1987
+ if (runs.length === 0) return [];
1988
+ return [(0, import_boxpdf.paragraph)({ width: contentWidth(node), align: node.style.textAlign, wrap: shouldWrap(node.style) }, ...runs)];
1989
+ }
1990
+ function renderList(node, options, warnings) {
1991
+ const items = node.children.filter((child) => !("text" in child) && child.node.tag === "li");
1992
+ const listPadding = edges(node.style.padding);
1993
+ const markerWidth = Math.max(node.style.fontSize * 1.5, listPadding.left * 0.65);
1994
+ const leftPadding = node.style.listStyleType === "none" ? listPadding.left : Math.max(0, listPadding.left - markerWidth);
1995
+ return (0, import_boxpdf.vstack)(
1996
+ {
1997
+ margin: node.style.margin,
1998
+ padding: { ...listPadding, left: leftPadding },
1999
+ gap: node.style.gap ?? 0
2000
+ },
2001
+ ...items.flatMap((item, index) => renderListItem(item, index, node.style.listStyleType, markerWidth, options, warnings))
2002
+ );
2003
+ }
2004
+ function renderListItem(item, index, listStyleType, markerWidth, options, warnings) {
2005
+ const runs = collectInlineRuns(item.children, options, warnings);
2006
+ const marker = listStyleType === "none" ? "" : listStyleType === "decimal" ? `${index + 1}. ` : "\u2022 ";
2007
+ const paragraphs = runs.length > 0 ? [
2008
+ (0, import_boxpdf.paragraph)(
2009
+ {
2010
+ width: item.style.width,
2011
+ align: item.style.textAlign,
2012
+ margin: item.style.margin,
2013
+ paddingLeft: marker ? markerWidth : 0,
2014
+ textIndent: marker ? -markerWidth : 0,
2015
+ wrap: shouldWrap(item.style)
2016
+ },
2017
+ ...marker ? [(0, import_boxpdf.run)(marker, runStyle({ style: item.style }, options))] : [],
2018
+ ...runs
2019
+ )
2020
+ ] : [];
2021
+ const blockChildren = item.children.filter((child) => !isInlineLike(child)).flatMap((child) => renderNode(child, options, warnings));
2022
+ return [...paragraphs, ...blockChildren];
2023
+ }
2024
+ function collectInlineRuns(nodes, options, warnings) {
2025
+ const runs = [];
2026
+ for (const node of nodes) {
2027
+ if ("text" in node) {
2028
+ if (preservesWhitespace(node.style) && node.text.length > 0) {
2029
+ runs.push((0, import_boxpdf.run)(node.text, runStyle(node, options)));
2030
+ } else if (node.text.trim()) {
2031
+ runs.push((0, import_boxpdf.run)(node.text, runStyle(node, options)));
2032
+ } else if (runs.length > 0) {
2033
+ runs.push((0, import_boxpdf.run)(" ", runStyle(node, options)));
2034
+ }
2035
+ continue;
2036
+ }
2037
+ if (node.node.tag === "br") {
2038
+ runs.push((0, import_boxpdf.run)("\n", runStyle({ style: node.style }, options)));
2039
+ continue;
2040
+ }
2041
+ if (node.node.tag === "img") {
2042
+ const rendered = renderImageForLayout(node, options, warnings);
2043
+ if (rendered) {
2044
+ const measured = (0, import_boxpdf.measure)(rendered, contentWidth(node) ?? options.width ?? Number.POSITIVE_INFINITY);
2045
+ runs.push((0, import_boxpdf.inlineNode)(rendered, { width: measured.width, height: measured.height, verticalAlign: node.style.verticalAlign === "middle" ? "middle" : void 0 }));
2046
+ }
2047
+ continue;
2048
+ }
2049
+ if (isAtomicInlineContainer(node)) {
2050
+ const rendered = renderAtomicInlineNode(node, options, warnings);
2051
+ const measured = (0, import_boxpdf.measure)(rendered, contentWidth(node) ?? options.width ?? Number.POSITIVE_INFINITY);
2052
+ runs.push((0, import_boxpdf.inlineNode)(rendered, { width: measured.width, height: measured.height, verticalAlign: node.style.verticalAlign === "middle" ? "middle" : void 0 }));
2053
+ continue;
2054
+ }
2055
+ if (node.style.display === "inline" || node.style.display === "contents") {
2056
+ runs.push(...collectInlineRuns(node.children, options, warnings));
2057
+ }
2058
+ }
2059
+ return runs;
2060
+ }
2061
+ function renderAtomicInlineNode(node, options, warnings) {
2062
+ if (node.style.display === "inline-flex") return renderFlex(node, options, warnings);
2063
+ if (node.style.display === "inline-grid") return renderGrid(node, options, warnings);
2064
+ return renderBlock(node, options, warnings);
2065
+ }
2066
+ function renderImageNode(node, options, warnings) {
2067
+ return renderImageContent(node, options, warnings, node.style.margin, true);
2068
+ }
2069
+ function renderImageForLayout(node, options, warnings) {
2070
+ if (!hasImageBoxStyling(node)) return renderImageNode(node, options, warnings);
2071
+ const content = renderImageContent(node, options, warnings, void 0, false);
2072
+ if (!content) return void 0;
2073
+ return (0, import_boxpdf.vstack)(
2074
+ {
2075
+ width: cssBoxWidth(node),
2076
+ height: cssBoxHeight(node),
2077
+ margin: node.style.margin,
2078
+ padding: layoutPadding(node),
2079
+ background: node.style.background,
2080
+ border: border(node),
2081
+ borderSides: node.style.borderSides,
2082
+ borderRadius: node.style.borderRadius,
2083
+ overflow: node.style.overflow,
2084
+ shrink: 0
2085
+ },
2086
+ content
2087
+ );
2088
+ }
2089
+ function renderImageContent(node, options, warnings, margin, decoratedFallback) {
2090
+ const src = imageUrl(node);
2091
+ if (!src) {
2092
+ warnings.push("img without a resolvable src was skipped");
2093
+ return void 0;
2094
+ }
2095
+ const pdfImage = options.resolveImage?.({ url: src, baseUrl: options.baseUrl });
2096
+ if (!pdfImage) {
2097
+ const size2 = imageSize(node, options);
2098
+ if (hasExplicitImageSize(node)) {
2099
+ warnings.push(`img src "${src}" did not resolve; preserved its layout box`);
2100
+ return (0, import_boxpdf.vstack)({
2101
+ width: size2.width,
2102
+ height: size2.height,
2103
+ margin,
2104
+ border: decoratedFallback ? border(node) : void 0,
2105
+ borderSides: decoratedFallback ? node.style.borderSides : void 0,
2106
+ borderRadius: decoratedFallback ? node.style.borderRadius : void 0,
2107
+ background: decoratedFallback ? node.style.background : void 0
2108
+ });
2109
+ }
2110
+ warnings.push(`img src "${src}" did not resolve`);
2111
+ return void 0;
2112
+ }
2113
+ const size = imageSize(node, options, pdfImage);
2114
+ if (node.style.objectFit === "contain" || node.style.objectFit === "cover") {
2115
+ return (0, import_boxpdf.imageFit)(pdfImage, { width: size.width, height: size.height, fit: node.style.objectFit, margin });
2116
+ }
2117
+ return (0, import_boxpdf.image)(pdfImage, { width: size.width, height: size.height, margin });
2118
+ }
2119
+ function hasExplicitImageSize(node) {
2120
+ return node.style.width !== void 0 || node.style.height !== void 0 || node.node.attrs.width !== void 0 || node.node.attrs.height !== void 0;
2121
+ }
2122
+ function hasImageBoxStyling(node) {
2123
+ return Boolean(
2124
+ node.style.background || node.style.borderWidth || node.style.borderSides || node.style.borderRadius || node.style.padding || node.style.overflow
2125
+ );
2126
+ }
2127
+ function imageSize(node, options, resolvedImage) {
2128
+ const src = imageUrl(node);
2129
+ const pdfImage = resolvedImage ?? (src ? options.resolveImage?.({ url: src, baseUrl: options.baseUrl }) : void 0);
2130
+ const naturalWidth = Math.max(1, (pdfImage?.width ?? 1) * 0.75);
2131
+ const naturalHeight = Math.max(1, (pdfImage?.height ?? 1) * 0.75);
2132
+ const naturalRatio = node.style.aspectRatio ?? naturalWidth / naturalHeight;
2133
+ const width = node.style.width;
2134
+ const height = node.style.height;
2135
+ if (width !== void 0 && height !== void 0) return { width, height };
2136
+ if (width !== void 0) return { width, height: width / naturalRatio };
2137
+ if (height !== void 0) return { width: height * naturalRatio, height };
2138
+ return { width: naturalWidth, height: naturalHeight };
2139
+ }
2140
+ function imageUrl(node) {
2141
+ if (node.node.parent?.tag === "picture") {
2142
+ for (const child of node.node.parent.children) {
2143
+ if (child.kind !== "element") continue;
2144
+ if (child === node.node) break;
2145
+ if (child.tag !== "source") continue;
2146
+ const selected = srcsetUrl(child.attrs.srcset, targetImageCssWidth(node));
2147
+ if (selected) return selected;
2148
+ }
2149
+ }
2150
+ return srcsetUrl(node.node.attrs.srcset, targetImageCssWidth(node)) ?? node.node.attrs.src;
2151
+ }
2152
+ function srcsetUrl(value, targetCssPx) {
2153
+ const candidates = srcsetCandidates(value);
2154
+ if (candidates.length === 0) return void 0;
2155
+ const widthCandidates = candidates.filter((candidate) => candidate.width !== void 0);
2156
+ if (widthCandidates.length > 0) {
2157
+ const sorted = widthCandidates.sort((a, b) => a.width - b.width);
2158
+ if (targetCssPx !== void 0) return sorted.find((candidate) => candidate.width >= targetCssPx)?.url ?? sorted[sorted.length - 1]?.url;
2159
+ return sorted[sorted.length - 1]?.url;
2160
+ }
2161
+ const densityCandidates = candidates.filter((candidate) => candidate.density !== void 0).sort((a, b) => a.density - b.density);
2162
+ if (densityCandidates.length > 0) return densityCandidates.find((candidate) => candidate.density >= 1)?.url ?? densityCandidates[densityCandidates.length - 1]?.url;
2163
+ return candidates[0]?.url;
2164
+ }
2165
+ function srcsetCandidates(value) {
2166
+ if (!value) return [];
2167
+ return value.split(",").map((raw) => raw.trim()).filter(Boolean).map((candidate) => {
2168
+ const [url, descriptor] = candidate.split(/\s+/, 2);
2169
+ if (!url) return void 0;
2170
+ if (descriptor?.endsWith("w")) {
2171
+ const width = Number.parseFloat(descriptor.slice(0, -1));
2172
+ return Number.isFinite(width) && width > 0 ? { url, width } : { url };
2173
+ }
2174
+ if (descriptor?.endsWith("x")) {
2175
+ const density = Number.parseFloat(descriptor.slice(0, -1));
2176
+ return Number.isFinite(density) && density > 0 ? { url, density } : { url };
2177
+ }
2178
+ return { url };
2179
+ }).filter((candidate) => candidate !== void 0);
2180
+ }
2181
+ function targetImageCssWidth(node) {
2182
+ return node.style.width === void 0 ? void 0 : node.style.width / 0.75;
2183
+ }
2184
+ function renderTable(node, options, warnings) {
2185
+ const rows = tableRows(node);
2186
+ if (rows.length === 0) {
2187
+ warnings.push("table without direct tr children was flattened as a block");
2188
+ return [renderBlock(node, options, warnings)];
2189
+ }
2190
+ return [
2191
+ (0, import_boxpdf.table)({
2192
+ width: cssBoxWidth(node) ?? options.width,
2193
+ columns: inferColumns(rows),
2194
+ columnGap: 0,
2195
+ borderCollapse: node.style.borderCollapse,
2196
+ margin: node.style.margin,
2197
+ rows: rows.map(
2198
+ (row) => row.children.filter((child) => !("text" in child) && (child.node.tag === "td" || child.node.tag === "th")).map((cell) => ({
2199
+ content: renderCellContent(cell, options, warnings),
2200
+ padding: layoutPadding(cell, 4),
2201
+ background: cell.style.background,
2202
+ backgroundImage: backgroundImage(cell, options),
2203
+ border: border(cell),
2204
+ borderSides: cell.style.borderSides,
2205
+ borderRadius: cell.style.borderRadius,
2206
+ overflow: cell.style.overflow,
2207
+ align: cell.style.textAlign,
2208
+ valign: cell.style.verticalAlign === "middle" ? "middle" : "top"
2209
+ }))
2210
+ )
2211
+ })
2212
+ ];
2213
+ }
2214
+ function renderCellContent(cell, options, warnings) {
2215
+ const children = renderBlockChildren(cell, options, warnings);
2216
+ if (children.length === 1) return children[0];
2217
+ return (0, import_boxpdf.vstack)({ gap: cell.style.gap ?? 0 }, ...children);
2218
+ }
2219
+ function tableRows(node) {
2220
+ const rows = [];
2221
+ for (const child of node.children) {
2222
+ if ("text" in child) continue;
2223
+ if (child.node.tag === "tr") rows.push(child);
2224
+ if (child.node.tag === "thead" || child.node.tag === "tbody" || child.node.tag === "tfoot") {
2225
+ rows.push(...child.children.filter((row) => !("text" in row) && row.node.tag === "tr"));
2226
+ }
2227
+ }
2228
+ return rows;
2229
+ }
2230
+ function isInlineLike(node) {
2231
+ return "text" in node || isInlineContainer(node);
2232
+ }
2233
+ function isInlineContainer(node) {
2234
+ return node.style.display === "inline" || isAtomicInlineContainer(node);
2235
+ }
2236
+ function isAtomicInlineContainer(node) {
2237
+ return node.style.display === "inline-block" || node.style.display === "inline-flex" || node.style.display === "inline-grid";
2238
+ }
2239
+ function inferColumns(rows) {
2240
+ const count = Math.max(
2241
+ 1,
2242
+ ...rows.map(
2243
+ (row) => row.children.filter((child) => !("text" in child) && (child.node.tag === "td" || child.node.tag === "th")).length
2244
+ )
2245
+ );
2246
+ return Array.from({ length: count }, () => ({ width: "1fr" }));
2247
+ }
2248
+ function cssBoxWidth(node) {
2249
+ if (node.style.width === void 0) return void 0;
2250
+ if (node.style.boxSizing === "border-box") return node.style.width;
2251
+ const padding = edges(node.style.padding);
2252
+ const borders = borderWidths(node);
2253
+ return node.style.width + padding.left + padding.right + borders.left + borders.right;
2254
+ }
2255
+ function cssBoxHeight(node) {
2256
+ if (node.style.height === void 0) return void 0;
2257
+ if (node.style.boxSizing === "border-box") return node.style.height;
2258
+ const padding = edges(node.style.padding);
2259
+ const borders = borderWidths(node);
2260
+ return node.style.height + padding.top + padding.bottom + borders.top + borders.bottom;
2261
+ }
2262
+ function contentWidth(node) {
2263
+ if (node.style.width !== void 0 && node.style.boxSizing === "border-box") {
2264
+ const padding = edges(node.style.padding);
2265
+ const borders = borderWidths(node);
2266
+ return Math.max(0, node.style.width - padding.left - padding.right - borders.left - borders.right);
2267
+ }
2268
+ return node.style.width;
2269
+ }
2270
+ function edges(input) {
2271
+ if (input === void 0) return { top: 0, right: 0, bottom: 0, left: 0 };
2272
+ if (typeof input === "number") return { top: input, right: input, bottom: input, left: input };
2273
+ return {
2274
+ top: input.top ?? 0,
2275
+ right: input.right ?? 0,
2276
+ bottom: input.bottom ?? 0,
2277
+ left: input.left ?? 0
2278
+ };
2279
+ }
2280
+ function borderWidths(node) {
2281
+ const all = node.style.borderWidth ?? 0;
2282
+ return {
2283
+ top: node.style.borderSides?.top?.width ?? all,
2284
+ right: node.style.borderSides?.right?.width ?? all,
2285
+ bottom: node.style.borderSides?.bottom?.width ?? all,
2286
+ left: node.style.borderSides?.left?.width ?? all
2287
+ };
2288
+ }
2289
+ function layoutPadding(node, fallback) {
2290
+ const padding = edges(node.style.padding ?? fallback);
2291
+ const borders = borderWidths(node);
2292
+ const out = {
2293
+ top: padding.top + borders.top,
2294
+ right: padding.right + borders.right,
2295
+ bottom: padding.bottom + borders.bottom,
2296
+ left: padding.left + borders.left
2297
+ };
2298
+ if (out.top === 0 && out.right === 0 && out.bottom === 0 && out.left === 0) return void 0;
2299
+ if (out.top === out.right && out.right === out.bottom && out.bottom === out.left) return out.top;
2300
+ return out;
2301
+ }
2302
+ function backgroundImage(node, options) {
2303
+ if (!node.style.backgroundImageUrl || !options.resolveImage) return void 0;
2304
+ const image = options.resolveImage({ url: node.style.backgroundImageUrl, baseUrl: options.baseUrl });
2305
+ if (!image) return void 0;
2306
+ const width = cssBoxWidth(node);
2307
+ const height = cssBoxHeight(node);
2308
+ if (width === void 0 || height === void 0 || width <= 0 || height <= 0) return void 0;
2309
+ const sizing = node.style.backgroundSize ?? "auto";
2310
+ const naturalWidth = image.width * 0.75;
2311
+ const naturalHeight = image.height * 0.75;
2312
+ const scale = sizing === "cover" ? Math.max(width / naturalWidth, height / naturalHeight) : sizing === "contain" ? Math.min(width / naturalWidth, height / naturalHeight) : 1;
2313
+ const imageWidth = naturalWidth * scale;
2314
+ const imageHeight = naturalHeight * scale;
2315
+ const x = node.style.backgroundPositionX ?? 0;
2316
+ const y = node.style.backgroundPositionY ?? 0;
2317
+ return {
2318
+ image,
2319
+ width: imageWidth,
2320
+ height: imageHeight,
2321
+ offsetX: (width - imageWidth) * x,
2322
+ offsetY: (height - imageHeight) * y,
2323
+ repeat: node.style.backgroundRepeat ?? "repeat"
2324
+ };
2325
+ }
2326
+ function textOptions(node, options) {
2327
+ return {
2328
+ ...runStyle(node, options),
2329
+ width: node.style.width,
2330
+ wrap: shouldWrap(node.style),
2331
+ align: node.style.textAlign,
2332
+ margin: node.style.margin
2333
+ };
2334
+ }
2335
+ function runStyle(node, options) {
2336
+ return {
2337
+ size: node.style.fontSize,
2338
+ font: fontFor(node, options),
2339
+ color: node.style.color ?? options.defaultColor,
2340
+ lineHeight: node.style.lineHeight,
2341
+ underline: node.style.textDecorationLine === "underline",
2342
+ strikethrough: node.style.textDecorationLine === "line-through"
2343
+ };
2344
+ }
2345
+ function fontFor(node, options) {
2346
+ const resolved = options.resolveFont?.({
2347
+ families: node.style.fontFamily ?? [],
2348
+ weight: node.style.fontWeight,
2349
+ style: node.style.fontStyle
2350
+ });
2351
+ if (resolved) return resolved;
2352
+ if (isBold(node.style.fontWeight)) return options.boldFont ?? options.font;
2353
+ if (node.style.fontStyle === "italic") return options.italicFont ?? options.font;
2354
+ return options.font;
2355
+ }
2356
+ function isBold(weight) {
2357
+ return weight === "bold" || typeof weight === "number" && weight >= 600;
2358
+ }
2359
+ function shouldWrap(style) {
2360
+ return style.whiteSpace !== "nowrap" && style.whiteSpace !== "pre";
2361
+ }
2362
+ function preservesWhitespace(style) {
2363
+ return style.whiteSpace === "pre" || style.whiteSpace === "pre-wrap";
2364
+ }
2365
+ function border(node) {
2366
+ if (!node.style.borderWidth || !node.style.borderColor) return void 0;
2367
+ return { width: node.style.borderWidth, color: node.style.borderColor };
2368
+ }
2369
+
2370
+ // src/style.ts
2371
+ var blockTags = /* @__PURE__ */ new Set([
2372
+ "address",
2373
+ "article",
2374
+ "aside",
2375
+ "blockquote",
2376
+ "body",
2377
+ "div",
2378
+ "dl",
2379
+ "fieldset",
2380
+ "figcaption",
2381
+ "figure",
2382
+ "footer",
2383
+ "form",
2384
+ "h1",
2385
+ "h2",
2386
+ "h3",
2387
+ "h4",
2388
+ "h5",
2389
+ "h6",
2390
+ "header",
2391
+ "hr",
2392
+ "li",
2393
+ "main",
2394
+ "nav",
2395
+ "ol",
2396
+ "p",
2397
+ "pre",
2398
+ "section",
2399
+ "table",
2400
+ "tbody",
2401
+ "td",
2402
+ "tfoot",
2403
+ "th",
2404
+ "thead",
2405
+ "tr",
2406
+ "ul"
2407
+ ]);
2408
+ function computeStyles(root, rules, base, containingWidth, unsupportedCss) {
2409
+ return styleElement(root, rules, base, containingWidth, unsupportedCss);
2410
+ }
2411
+ function styleNode(node, rules, inherited, containingWidth, unsupportedCss, parentDisplay) {
2412
+ if (node.kind === "text") {
2413
+ return { node, style: inherited, text: transformText(normalizeWhitespace(node.value, inherited.whiteSpace), inherited.textTransform) };
2414
+ }
2415
+ return styleElement(node, rules, inherited, containingWidth, unsupportedCss, parentDisplay);
2416
+ }
2417
+ function styleElement(node, rules, inherited, containingWidth, unsupportedCss, parentDisplay) {
2418
+ const tagDefaults = defaultsForTag(node.tag, inherited);
2419
+ const ruleDeclarations = ruleDeclarationsFor(node, rules, tagDefaults.fontSize, tagDefaults.customProperties, unsupportedCss);
2420
+ const withRules = mergeStyles(tagDefaults, ruleDeclarations.declarations);
2421
+ const inlineDeclarations = parseStyleAttribute(node.attrs.style, withRules.fontSize, withRules.customProperties, unsupportedCss);
2422
+ const style = mergeStyles(
2423
+ withRules,
2424
+ inlineDeclarations.declarations,
2425
+ ruleDeclarations.importantDeclarations,
2426
+ inlineDeclarations.importantDeclarations
2427
+ );
2428
+ if (style.widthPercent !== void 0 && containingWidth !== void 0) style.width = containingWidth * style.widthPercent;
2429
+ if (style.minWidthPercent !== void 0 && containingWidth !== void 0) style.minWidth = containingWidth * style.minWidthPercent;
2430
+ if (style.maxWidthPercent !== void 0 && containingWidth !== void 0) style.maxWidth = containingWidth * style.maxWidthPercent;
2431
+ if (style.widthCalc !== void 0 && containingWidth !== void 0) style.width = containingWidth * style.widthCalc.percent + style.widthCalc.length;
2432
+ if (style.minWidthCalc !== void 0 && containingWidth !== void 0) style.minWidth = containingWidth * style.minWidthCalc.percent + style.minWidthCalc.length;
2433
+ if (style.maxWidthCalc !== void 0 && containingWidth !== void 0) style.maxWidth = containingWidth * style.maxWidthCalc.percent + style.maxWidthCalc.length;
2434
+ if ((style.display === "block" || style.display === "flex" || style.display === "grid") && parentDisplay !== "flex" && parentDisplay !== "grid" && style.width === void 0 && containingWidth !== void 0) {
2435
+ style.width = autoContentWidth(style, containingWidth);
2436
+ }
2437
+ if (node.tag === "img") {
2438
+ style.width ??= parseDimensionAttr(node.attrs.width);
2439
+ style.height ??= parseDimensionAttr(node.attrs.height);
2440
+ }
2441
+ if (style.width === void 0 && containingWidth !== void 0 && (style.minWidth !== void 0 || style.maxWidth !== void 0)) {
2442
+ style.width = containingWidth;
2443
+ }
2444
+ if (style.width !== void 0) style.width = clamp(style.width, style.minWidth, style.maxWidth);
2445
+ applyAspectRatio(style);
2446
+ applyAutoMargins(style, containingWidth);
2447
+ if (style.lineHeightScale !== void 0) style.lineHeight = style.fontSize * style.lineHeightScale;
2448
+ const inheritedForChildren = inherit(style);
2449
+ const childContainingWidth = parentDisplay === "flex" && style.width === void 0 ? void 0 : contentWidthForChildren(style, containingWidth);
2450
+ const children = node.children.map((child) => styleNode(child, rules, inheritedForChildren, childContainingWidth, unsupportedCss, style.display)).filter((child) => child !== void 0);
2451
+ return { node, style, children };
2452
+ }
2453
+ function defaultStyle(fontSize = 12) {
2454
+ return {
2455
+ display: "block",
2456
+ flexDirection: "row",
2457
+ alignItems: "stretch",
2458
+ justifyContent: "start",
2459
+ fontSize,
2460
+ fontWeight: "normal",
2461
+ fontStyle: "normal",
2462
+ textAlign: "left",
2463
+ verticalAlign: "baseline",
2464
+ boxSizing: "content-box",
2465
+ whiteSpace: "normal"
2466
+ };
2467
+ }
2468
+ function defaultsForTag(tag, inherited) {
2469
+ const style = {
2470
+ ...inherited,
2471
+ display: blockTags.has(tag) ? "block" : "inline",
2472
+ float: void 0,
2473
+ order: void 0,
2474
+ margin: void 0,
2475
+ marginAutoLeft: void 0,
2476
+ marginAutoRight: void 0,
2477
+ padding: void 0,
2478
+ background: void 0,
2479
+ backgroundImageUrl: void 0,
2480
+ backgroundSize: void 0,
2481
+ backgroundRepeat: void 0,
2482
+ backgroundPositionX: void 0,
2483
+ backgroundPositionY: void 0,
2484
+ objectFit: void 0,
2485
+ overflow: void 0,
2486
+ borderWidth: void 0,
2487
+ borderColor: void 0,
2488
+ borderSides: void 0,
2489
+ borderRadius: void 0,
2490
+ boxSizing: "content-box",
2491
+ width: void 0,
2492
+ widthPercent: void 0,
2493
+ widthCalc: void 0,
2494
+ minWidth: void 0,
2495
+ minWidthPercent: void 0,
2496
+ minWidthCalc: void 0,
2497
+ maxWidth: void 0,
2498
+ maxWidthPercent: void 0,
2499
+ maxWidthCalc: void 0,
2500
+ height: void 0,
2501
+ aspectRatio: void 0,
2502
+ position: void 0,
2503
+ top: void 0,
2504
+ right: void 0,
2505
+ bottom: void 0,
2506
+ left: void 0,
2507
+ zIndex: void 0,
2508
+ columnGap: void 0,
2509
+ rowGap: void 0,
2510
+ gridTemplateColumns: void 0,
2511
+ gridColumnStart: void 0,
2512
+ gridColumnEnd: void 0,
2513
+ gridColumnSpan: void 0
2514
+ };
2515
+ if (tag === "strong" || tag === "b" || tag === "th") style.fontWeight = "bold";
2516
+ if (tag === "em" || tag === "i") style.fontStyle = "italic";
2517
+ if (tag === "h1") {
2518
+ Object.assign(style, {
2519
+ fontSize: inherited.fontSize * 2,
2520
+ fontWeight: "bold",
2521
+ margin: { top: 0.67 * inherited.fontSize * 2, bottom: 0.67 * inherited.fontSize * 2 }
2522
+ });
2523
+ }
2524
+ if (tag === "h2") {
2525
+ Object.assign(style, {
2526
+ fontSize: inherited.fontSize * 1.5,
2527
+ fontWeight: "bold",
2528
+ margin: { top: 0.83 * inherited.fontSize * 1.5, bottom: 0.83 * inherited.fontSize * 1.5 }
2529
+ });
2530
+ }
2531
+ if (tag === "h3") {
2532
+ Object.assign(style, {
2533
+ fontSize: inherited.fontSize * 1.17,
2534
+ fontWeight: "bold",
2535
+ margin: { top: inherited.fontSize * 1.17, bottom: inherited.fontSize * 1.17 }
2536
+ });
2537
+ }
2538
+ if (tag === "p") style.margin = { top: inherited.fontSize, bottom: inherited.fontSize };
2539
+ if (tag === "ul" || tag === "ol") {
2540
+ style.margin = { top: inherited.fontSize, bottom: inherited.fontSize };
2541
+ style.padding = { left: inherited.fontSize * 2.5 };
2542
+ style.listStyleType = tag === "ol" ? "decimal" : "disc";
2543
+ }
2544
+ if (tag === "li") style.display = "block";
2545
+ if (tag === "img") style.display = "inline";
2546
+ if (tag === "pre") style.whiteSpace = "pre";
2547
+ if (tag === "br") style.display = "inline";
2548
+ return style;
2549
+ }
2550
+ function inherit(style) {
2551
+ return {
2552
+ ...style,
2553
+ display: "inline",
2554
+ float: void 0,
2555
+ order: void 0,
2556
+ margin: void 0,
2557
+ marginAutoLeft: void 0,
2558
+ marginAutoRight: void 0,
2559
+ padding: void 0,
2560
+ background: void 0,
2561
+ backgroundImageUrl: void 0,
2562
+ backgroundSize: void 0,
2563
+ backgroundRepeat: void 0,
2564
+ backgroundPositionX: void 0,
2565
+ backgroundPositionY: void 0,
2566
+ objectFit: void 0,
2567
+ overflow: void 0,
2568
+ borderWidth: void 0,
2569
+ borderColor: void 0,
2570
+ borderSides: void 0,
2571
+ borderRadius: void 0,
2572
+ boxSizing: "content-box",
2573
+ width: void 0,
2574
+ widthPercent: void 0,
2575
+ widthCalc: void 0,
2576
+ minWidth: void 0,
2577
+ minWidthPercent: void 0,
2578
+ minWidthCalc: void 0,
2579
+ maxWidth: void 0,
2580
+ maxWidthPercent: void 0,
2581
+ maxWidthCalc: void 0,
2582
+ height: void 0,
2583
+ aspectRatio: void 0,
2584
+ position: void 0,
2585
+ top: void 0,
2586
+ right: void 0,
2587
+ bottom: void 0,
2588
+ left: void 0,
2589
+ zIndex: void 0,
2590
+ columnGap: void 0,
2591
+ rowGap: void 0,
2592
+ gridTemplateColumns: void 0,
2593
+ gridColumnStart: void 0,
2594
+ gridColumnEnd: void 0,
2595
+ gridColumnSpan: void 0
2596
+ };
2597
+ }
2598
+ function applyAspectRatio(style) {
2599
+ if (style.aspectRatio === void 0 || style.aspectRatio <= 0) return;
2600
+ if (style.width !== void 0 && style.height === void 0) style.height = style.width / style.aspectRatio;
2601
+ else if (style.height !== void 0 && style.width === void 0) style.width = style.height * style.aspectRatio;
2602
+ }
2603
+ function parseDimensionAttr(value) {
2604
+ if (!value) return void 0;
2605
+ const normalized = value.trim();
2606
+ if (/^\d+(?:\.\d+)?$/.test(normalized)) return Number(normalized) * 0.75;
2607
+ return void 0;
2608
+ }
2609
+ function mergeStyles(...styles) {
2610
+ const out = {};
2611
+ for (const style of styles) {
2612
+ const borderSides = out.borderSides;
2613
+ Object.assign(out, style);
2614
+ if (style.borderSides) {
2615
+ out.borderSides = { ...borderSides, ...style.borderSides };
2616
+ }
2617
+ }
2618
+ return out;
2619
+ }
2620
+ function contentWidthForChildren(style, containingWidth) {
2621
+ if (style.width !== void 0) return contentWidth2(style);
2622
+ if (containingWidth === void 0) return void 0;
2623
+ const padding = edges2(style.padding);
2624
+ const borders = borderWidths2(style);
2625
+ return Math.max(0, containingWidth - padding.left - padding.right - borders.left - borders.right);
2626
+ }
2627
+ function autoContentWidth(style, containingWidth) {
2628
+ if (style.boxSizing === "border-box") return containingWidth;
2629
+ const padding = edges2(style.padding);
2630
+ const borders = borderWidths2(style);
2631
+ return Math.max(0, containingWidth - padding.left - padding.right - borders.left - borders.right);
2632
+ }
2633
+ function applyAutoMargins(style, containingWidth) {
2634
+ if (containingWidth === void 0 || style.width === void 0 || !style.marginAutoLeft && !style.marginAutoRight) return;
2635
+ const margin = edges2(style.margin);
2636
+ const remaining = Math.max(0, containingWidth - outerWidth(style) + margin.left + margin.right);
2637
+ if (style.marginAutoLeft && style.marginAutoRight) {
2638
+ style.margin = { ...margin, left: remaining / 2, right: remaining / 2 };
2639
+ } else if (style.marginAutoLeft) {
2640
+ style.margin = { ...margin, left: remaining };
2641
+ } else if (style.marginAutoRight) {
2642
+ style.margin = { ...margin, right: remaining };
2643
+ }
2644
+ }
2645
+ function outerWidth(style) {
2646
+ const margin = edges2(style.margin);
2647
+ const padding = edges2(style.padding);
2648
+ const borders = borderWidths2(style);
2649
+ const box = style.boxSizing === "border-box" ? style.width ?? 0 : (style.width ?? 0) + padding.left + padding.right + borders.left + borders.right;
2650
+ return box + margin.left + margin.right;
2651
+ }
2652
+ function contentWidth2(style) {
2653
+ if (style.width === void 0 || style.boxSizing !== "border-box") return style.width ?? 0;
2654
+ const padding = edges2(style.padding);
2655
+ const borders = borderWidths2(style);
2656
+ return Math.max(0, style.width - padding.left - padding.right - borders.left - borders.right);
2657
+ }
2658
+ function clamp(value, min, max) {
2659
+ if (min !== void 0) value = Math.max(value, min);
2660
+ if (max !== void 0) value = Math.min(value, max);
2661
+ return value;
2662
+ }
2663
+ function edges2(input) {
2664
+ if (input === void 0) return { top: 0, right: 0, bottom: 0, left: 0 };
2665
+ if (typeof input === "number") return { top: input, right: input, bottom: input, left: input };
2666
+ return {
2667
+ top: input.top ?? 0,
2668
+ right: input.right ?? 0,
2669
+ bottom: input.bottom ?? 0,
2670
+ left: input.left ?? 0
2671
+ };
2672
+ }
2673
+ function borderWidths2(style) {
2674
+ const all = style.borderWidth ?? 0;
2675
+ return {
2676
+ top: style.borderSides?.top?.width ?? all,
2677
+ right: style.borderSides?.right?.width ?? all,
2678
+ bottom: style.borderSides?.bottom?.width ?? all,
2679
+ left: style.borderSides?.left?.width ?? all
2680
+ };
2681
+ }
2682
+ function normalizeWhitespace(value, whiteSpace) {
2683
+ const normalized = value.replace(/\r\n?/g, "\n");
2684
+ if (whiteSpace === "pre" || whiteSpace === "pre-wrap") return normalized;
2685
+ if (whiteSpace === "pre-line") return normalized.replace(/[^\S\n]+/g, " ");
2686
+ return normalized.replace(/\s+/g, " ");
2687
+ }
2688
+ function transformText(value, transform) {
2689
+ if (transform === "uppercase") return value.toUpperCase();
2690
+ if (transform === "lowercase") return value.toLowerCase();
2691
+ if (transform === "capitalize") {
2692
+ return value.replace(/\p{L}[\p{L}\p{N}'-]*/gu, (word) => word[0].toUpperCase() + word.slice(1).toLowerCase());
2693
+ }
2694
+ return value;
2695
+ }
2696
+
2697
+ // src/font.ts
2698
+ function fontFamily(families) {
2699
+ const normalized = /* @__PURE__ */ new Map();
2700
+ for (const [name, face] of Object.entries(families)) {
2701
+ normalized.set(normalizeFamily(name), face);
2702
+ }
2703
+ return (request) => {
2704
+ for (const family of request.families) {
2705
+ const face = normalized.get(normalizeFamily(family));
2706
+ const font = face && resolveFace(face, request);
2707
+ if (font) return font;
2708
+ }
2709
+ return void 0;
2710
+ };
2711
+ }
2712
+ function resolveFace(face, request) {
2713
+ if (isPdfFont(face)) return face;
2714
+ const preferred = preferredKeys(request.weight, request.style);
2715
+ for (const key of preferred) {
2716
+ const font = face[key];
2717
+ if (font) return font;
2718
+ }
2719
+ return face.normal ?? face.bold ?? face.italic;
2720
+ }
2721
+ function preferredKeys(weight, style) {
2722
+ const keys = [];
2723
+ if (typeof weight === "number") keys.push(`${weight}`);
2724
+ if (isBold2(weight) && style === "italic") keys.push("boldItalic");
2725
+ if (style === "italic") keys.push("italic");
2726
+ if (isBold2(weight)) keys.push("bold");
2727
+ keys.push("normal");
2728
+ return keys;
2729
+ }
2730
+ function isBold2(weight) {
2731
+ return weight === "bold" || typeof weight === "number" && weight >= 600;
2732
+ }
2733
+ function normalizeFamily(family) {
2734
+ return family.trim().replace(/^['"]|['"]$/g, "").toLowerCase();
2735
+ }
2736
+ function isPdfFont(value) {
2737
+ return "embedder" in value;
2738
+ }
2739
+
2740
+ // src/index.ts
2741
+ function htmlToBoxpdf(html, options) {
2742
+ const startedAt = now();
2743
+ const profile = (event) => {
2744
+ options.profile?.({ ...event, elapsedMs: now() - startedAt });
2745
+ };
2746
+ profile({ phase: "start", htmlBytes: byteLength(html) });
2747
+ const parsed = parseHtml(html);
2748
+ profile({ phase: "parse-html", domNodes: countDomNodes(parsed.root), stylesheets: parsed.stylesheets.length });
2749
+ const rules = parseStylesheets(parsed.stylesheets);
2750
+ profile({ phase: "parse-css", cssRules: rules.length });
2751
+ const diagnostics = createDiagnostics(options);
2752
+ const styled = computeStyles(parsed.root, rules, {
2753
+ ...defaultStyle(options.defaultFontSize ?? 12),
2754
+ color: options.defaultColor,
2755
+ lineHeight: options.defaultLineHeight
2756
+ }, options.width, diagnostics ? (declaration) => diagnostics.recordUnsupportedCss(declaration) : void 0);
2757
+ profile({ phase: "compute-styles", styledNodes: countStyledNodes(styled) });
2758
+ const result = renderStyledTree(styled, options);
2759
+ if (diagnostics) result.diagnostics = diagnostics.toJSON();
2760
+ profile({ phase: "render-tree", ...countBoxNodes(result.nodes) });
2761
+ profile({ phase: "finish" });
2762
+ return result;
2763
+ }
2764
+ function createDiagnostics(options) {
2765
+ if (!options.diagnostics?.unsupportedCss) return void 0;
2766
+ const sampleLimit = options.diagnostics.sampleLimit ?? 3;
2767
+ const unsupported = /* @__PURE__ */ new Map();
2768
+ return {
2769
+ recordUnsupportedCss(declaration) {
2770
+ const property = declaration.property.trim().toLowerCase();
2771
+ const value = declaration.value.trim();
2772
+ const key = `${property}
2773
+ ${value}`;
2774
+ const entry = unsupported.get(key) ?? { property, value, count: 0, samples: [] };
2775
+ entry.count += 1;
2776
+ const sample = declaration.selector ? `${declaration.selector} { ${property}: ${value} }` : `${property}: ${value}`;
2777
+ if (entry.samples.length < sampleLimit && !entry.samples.includes(sample)) entry.samples.push(sample);
2778
+ unsupported.set(key, entry);
2779
+ },
2780
+ toJSON() {
2781
+ return {
2782
+ unsupportedCss: [...unsupported.values()].sort((a, b) => b.count - a.count || a.property.localeCompare(b.property)).map(({ property, value, count, samples }) => ({ property, value, count, samples: samples.length > 0 ? samples : void 0 }))
2783
+ };
2784
+ }
2785
+ };
2786
+ }
2787
+ function now() {
2788
+ return typeof performance === "undefined" ? Date.now() : performance.now();
2789
+ }
2790
+ function byteLength(value) {
2791
+ return typeof TextEncoder === "undefined" ? value.length : new TextEncoder().encode(value).length;
2792
+ }
2793
+ function countDomNodes(node) {
2794
+ if (node.kind === "text") return 1;
2795
+ return 1 + node.children.reduce((sum, child) => sum + countDomNodes(child), 0);
2796
+ }
2797
+ function countStyledNodes(node) {
2798
+ if ("text" in node) return 1;
2799
+ return 1 + node.children.reduce((sum, child) => sum + countStyledNodes(child), 0);
2800
+ }
2801
+ function countBoxNodes(nodes) {
2802
+ return nodes.reduce(
2803
+ (sum, node) => {
2804
+ const counted = countBoxNode(node);
2805
+ return {
2806
+ boxNodes: sum.boxNodes + counted.boxNodes,
2807
+ paragraphs: sum.paragraphs + counted.paragraphs,
2808
+ textRuns: sum.textRuns + counted.textRuns
2809
+ };
2810
+ },
2811
+ { boxNodes: 0, paragraphs: 0, textRuns: 0 }
2812
+ );
2813
+ }
2814
+ function countBoxNode(node) {
2815
+ let out = {
2816
+ boxNodes: 1,
2817
+ paragraphs: node.kind === "paragraph" ? 1 : 0,
2818
+ textRuns: node.kind === "paragraph" ? node.runs.filter((run2) => "text" in run2).length : node.kind === "text" ? 1 : 0
2819
+ };
2820
+ if ("children" in node) {
2821
+ for (const child of node.children) {
2822
+ const counted = countBoxNode(child);
2823
+ out = {
2824
+ boxNodes: out.boxNodes + counted.boxNodes,
2825
+ paragraphs: out.paragraphs + counted.paragraphs,
2826
+ textRuns: out.textRuns + counted.textRuns
2827
+ };
2828
+ }
2829
+ }
2830
+ return out;
2831
+ }
2832
+ // Annotate the CommonJS export names for ESM import in node:
2833
+ 0 && (module.exports = {
2834
+ fontFamily,
2835
+ htmlToBoxpdf,
2836
+ parseHtml
2837
+ });
2838
+ //# sourceMappingURL=index.cjs.map