domotion-svg 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/FEATURES.md +102 -0
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/animator.d.ts +158 -0
- package/dist/animator.js +424 -0
- package/dist/animator.test.d.ts +5 -0
- package/dist/animator.test.js +169 -0
- package/dist/border-radius.test.d.ts +1 -0
- package/dist/border-radius.test.js +148 -0
- package/dist/capture.d.ts +193 -0
- package/dist/capture.js +786 -0
- package/dist/chrome.d.ts +45 -0
- package/dist/chrome.js +107 -0
- package/dist/cli.d.ts +16 -0
- package/dist/cli.js +512 -0
- package/dist/client/dom.d.ts +10 -0
- package/dist/client/dom.js +17 -0
- package/dist/conic-raster.d.ts +58 -0
- package/dist/conic-raster.js +292 -0
- package/dist/conic-raster.test.d.ts +1 -0
- package/dist/conic-raster.test.js +187 -0
- package/dist/coretext-extractor.test.d.ts +1 -0
- package/dist/coretext-extractor.test.js +94 -0
- package/dist/coretext-helper.d.ts +60 -0
- package/dist/coretext-helper.js +205 -0
- package/dist/cross-origin-font-face.test.d.ts +1 -0
- package/dist/cross-origin-font-face.test.js +107 -0
- package/dist/cursor-overlay.d.ts +123 -0
- package/dist/cursor-overlay.js +207 -0
- package/dist/cursor-overlay.test.d.ts +1 -0
- package/dist/cursor-overlay.test.js +88 -0
- package/dist/dark-mode-capture.test.d.ts +1 -0
- package/dist/dark-mode-capture.test.js +158 -0
- package/dist/dark-mode-form-controls.test.d.ts +1 -0
- package/dist/dark-mode-form-controls.test.js +218 -0
- package/dist/dom-to-svg.d.ts +1016 -0
- package/dist/dom-to-svg.js +7717 -0
- package/dist/embed-remote-images.test.d.ts +1 -0
- package/dist/embed-remote-images.test.js +424 -0
- package/dist/form-controls.d.ts +70 -0
- package/dist/form-controls.js +1151 -0
- package/dist/frame-merge.d.ts +95 -0
- package/dist/frame-merge.js +374 -0
- package/dist/frame-merge.test.d.ts +6 -0
- package/dist/frame-merge.test.js +144 -0
- package/dist/gradients.d.ts +184 -0
- package/dist/gradients.js +937 -0
- package/dist/gradients.test.d.ts +1 -0
- package/dist/gradients.test.js +150 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +7 -0
- package/dist/jsx-runtime.d.ts +27 -0
- package/dist/jsx-runtime.js +96 -0
- package/dist/jsx-runtime.test.d.ts +1 -0
- package/dist/jsx-runtime.test.js +41 -0
- package/dist/kerfjs-imports.test.d.ts +1 -0
- package/dist/kerfjs-imports.test.js +36 -0
- package/dist/mask.test.d.ts +1 -0
- package/dist/mask.test.js +206 -0
- package/dist/optimize.d.ts +12 -0
- package/dist/optimize.js +32 -0
- package/dist/preserve-aspect-ratio.test.d.ts +1 -0
- package/dist/preserve-aspect-ratio.test.js +38 -0
- package/dist/resize-embedded-images.d.ts +33 -0
- package/dist/resize-embedded-images.js +164 -0
- package/dist/resize-embedded-images.test.d.ts +9 -0
- package/dist/resize-embedded-images.test.js +255 -0
- package/dist/stacking-context.test.d.ts +1 -0
- package/dist/stacking-context.test.js +927 -0
- package/dist/text-renderer.d.ts +42 -0
- package/dist/text-renderer.js +608 -0
- package/dist/text-renderer.test.d.ts +1 -0
- package/dist/text-renderer.test.js +150 -0
- package/dist/text-to-path.d.ts +265 -0
- package/dist/text-to-path.js +1800 -0
- package/dist/text-to-path.test.d.ts +1 -0
- package/dist/text-to-path.test.js +570 -0
- package/dist/utils/escapeHtml.d.ts +2 -0
- package/dist/utils/escapeHtml.js +15 -0
- package/dist/webfont-unicode-range.test.d.ts +1 -0
- package/dist/webfont-unicode-range.test.js +174 -0
- package/package.json +55 -0
- package/src/animator.test.ts +179 -0
- package/src/animator.ts +660 -0
- package/src/border-radius.test.ts +160 -0
- package/src/capture.ts +810 -0
- package/src/cli.ts +582 -0
- package/src/conic-raster.test.ts +213 -0
- package/src/conic-raster.ts +309 -0
- package/src/coretext-extractor.test.ts +130 -0
- package/src/coretext-helper.ts +256 -0
- package/src/cross-origin-font-face.test.ts +119 -0
- package/src/cursor-overlay.test.ts +95 -0
- package/src/cursor-overlay.ts +297 -0
- package/src/dark-mode-capture.test.ts +177 -0
- package/src/dark-mode-form-controls.test.ts +228 -0
- package/src/dom-to-svg.ts +8376 -0
- package/src/embed-remote-images.test.ts +461 -0
- package/src/form-controls.ts +1174 -0
- package/src/frame-merge.test.ts +157 -0
- package/src/frame-merge.ts +447 -0
- package/src/globals.d.ts +2 -0
- package/src/gradients.test.ts +175 -0
- package/src/gradients.ts +955 -0
- package/src/index.ts +12 -0
- package/src/kerf-jsx-augmentation.d.ts +36 -0
- package/src/kerfjs-imports.test.tsx +45 -0
- package/src/mask.test.ts +274 -0
- package/src/optimize.ts +34 -0
- package/src/preserve-aspect-ratio.test.ts +49 -0
- package/src/resize-embedded-images.test.ts +292 -0
- package/src/resize-embedded-images.ts +180 -0
- package/src/stacking-context.test.ts +967 -0
- package/src/text-renderer.test.ts +162 -0
- package/src/text-renderer.ts +623 -0
- package/src/text-to-path.test.ts +639 -0
- package/src/text-to-path.ts +1810 -0
- package/src/utils/escapeHtml.ts +16 -0
- package/src/webfont-unicode-range.test.ts +207 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { insetCornerRadii, parseCornerRadii, roundedRectPath, roundedRectSvg } from "./dom-to-svg.js";
|
|
3
|
+
describe("parseCornerRadii: shorthand and longhand", () => {
|
|
4
|
+
it("treats four equal circular corners as uniform", () => {
|
|
5
|
+
const c = parseCornerRadii({
|
|
6
|
+
borderTopLeftRadius: "10px 10px",
|
|
7
|
+
borderTopRightRadius: "10px 10px",
|
|
8
|
+
borderBottomRightRadius: "10px 10px",
|
|
9
|
+
borderBottomLeftRadius: "10px 10px",
|
|
10
|
+
}, 200, 100);
|
|
11
|
+
expect(c.uniform).toBe(true);
|
|
12
|
+
expect(c.tl).toEqual({ h: 10, v: 10 });
|
|
13
|
+
});
|
|
14
|
+
it("flags asymmetric per-corner radii as non-uniform (DM-300)", () => {
|
|
15
|
+
const c = parseCornerRadii({
|
|
16
|
+
borderTopLeftRadius: "10px 10px",
|
|
17
|
+
borderTopRightRadius: "30px 30px",
|
|
18
|
+
borderBottomRightRadius: "50px 50px",
|
|
19
|
+
borderBottomLeftRadius: "70px 70px",
|
|
20
|
+
}, 200, 100);
|
|
21
|
+
expect(c.uniform).toBe(false);
|
|
22
|
+
expect(c.tl.h).toBe(10);
|
|
23
|
+
expect(c.tr.h).toBe(30);
|
|
24
|
+
expect(c.br.h).toBe(50);
|
|
25
|
+
expect(c.bl.h).toBe(70);
|
|
26
|
+
});
|
|
27
|
+
it("flags elliptical corners as non-uniform even when all four are equal", () => {
|
|
28
|
+
const c = parseCornerRadii({
|
|
29
|
+
borderTopLeftRadius: "50px 20px",
|
|
30
|
+
borderTopRightRadius: "50px 20px",
|
|
31
|
+
borderBottomRightRadius: "50px 20px",
|
|
32
|
+
borderBottomLeftRadius: "50px 20px",
|
|
33
|
+
}, 400, 80);
|
|
34
|
+
expect(c.uniform).toBe(false);
|
|
35
|
+
expect(c.tl).toEqual({ h: 50, v: 20 });
|
|
36
|
+
});
|
|
37
|
+
it("falls back to the borderRadius shorthand when longhands aren't captured", () => {
|
|
38
|
+
const c = parseCornerRadii({ borderRadius: "12px" }, 100, 100);
|
|
39
|
+
expect(c.uniform).toBe(true);
|
|
40
|
+
expect(c.tl).toEqual({ h: 12, v: 12 });
|
|
41
|
+
});
|
|
42
|
+
it("scales corners down to fit the edge length (CSS corner-overlap §5.5)", () => {
|
|
43
|
+
// 999px corners on a 200x100 box: top edge has rTL.h + rTR.h = 1998 > 200,
|
|
44
|
+
// so all corners scale by 200/1998 ≈ 0.1. The vertical sums (rTR.v +
|
|
45
|
+
// rBR.v = 1998 > 100, etc.) drive a tighter scale of 100/1998 ≈ 0.05,
|
|
46
|
+
// which wins. Final per-corner radius: ~50 (half the box height).
|
|
47
|
+
const c = parseCornerRadii({
|
|
48
|
+
borderTopLeftRadius: "999px 999px",
|
|
49
|
+
borderTopRightRadius: "999px 999px",
|
|
50
|
+
borderBottomRightRadius: "999px 999px",
|
|
51
|
+
borderBottomLeftRadius: "999px 999px",
|
|
52
|
+
}, 200, 100);
|
|
53
|
+
expect(c.tl.h).toBeCloseTo(50, 0);
|
|
54
|
+
expect(c.tl.v).toBeCloseTo(50, 0);
|
|
55
|
+
expect(c.uniform).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe("insetCornerRadii: inner-corner derivation", () => {
|
|
59
|
+
it("shrinks each corner by the matching adjacent border-side widths", () => {
|
|
60
|
+
const c = parseCornerRadii({
|
|
61
|
+
borderTopLeftRadius: "20px 20px",
|
|
62
|
+
borderTopRightRadius: "20px 20px",
|
|
63
|
+
borderBottomRightRadius: "20px 20px",
|
|
64
|
+
borderBottomLeftRadius: "20px 20px",
|
|
65
|
+
}, 100, 100);
|
|
66
|
+
const inner = insetCornerRadii(c, 5, 3, 5, 3);
|
|
67
|
+
// TL: shrink by left=3 (h) and top=5 (v).
|
|
68
|
+
expect(inner.tl).toEqual({ h: 17, v: 15 });
|
|
69
|
+
// TR: shrink by right=3 (h) and top=5 (v).
|
|
70
|
+
expect(inner.tr).toEqual({ h: 17, v: 15 });
|
|
71
|
+
});
|
|
72
|
+
it("clamps shrunk corners to zero rather than going negative", () => {
|
|
73
|
+
const c = parseCornerRadii({
|
|
74
|
+
borderTopLeftRadius: "4px 4px",
|
|
75
|
+
borderTopRightRadius: "4px 4px",
|
|
76
|
+
borderBottomRightRadius: "4px 4px",
|
|
77
|
+
borderBottomLeftRadius: "4px 4px",
|
|
78
|
+
}, 100, 100);
|
|
79
|
+
const inner = insetCornerRadii(c, 10, 10, 10, 10);
|
|
80
|
+
expect(inner.tl).toEqual({ h: 0, v: 0 });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe("roundedRectPath: SVG d-attribute geometry", () => {
|
|
84
|
+
it("emits a clockwise path with one elliptical arc per corner", () => {
|
|
85
|
+
const c = parseCornerRadii({
|
|
86
|
+
borderTopLeftRadius: "10px 10px",
|
|
87
|
+
borderTopRightRadius: "20px 20px",
|
|
88
|
+
borderBottomRightRadius: "30px 30px",
|
|
89
|
+
borderBottomLeftRadius: "40px 40px",
|
|
90
|
+
}, 200, 100);
|
|
91
|
+
const d = roundedRectPath(0, 0, 200, 100, c);
|
|
92
|
+
// Starts at (TL.h, 0).
|
|
93
|
+
expect(d.startsWith("M10,0 ")).toBe(true);
|
|
94
|
+
// Has 4 elliptical arc commands, one per non-zero corner.
|
|
95
|
+
const arcs = d.match(/A/g) || [];
|
|
96
|
+
expect(arcs.length).toBe(4);
|
|
97
|
+
// Closes the path.
|
|
98
|
+
expect(d.endsWith(" Z")).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
it("omits the arc for a zero-radius corner so adjacent lines meet sharply", () => {
|
|
101
|
+
const c = parseCornerRadii({
|
|
102
|
+
borderTopLeftRadius: "0px 0px",
|
|
103
|
+
borderTopRightRadius: "20px 20px",
|
|
104
|
+
borderBottomRightRadius: "0px 0px",
|
|
105
|
+
borderBottomLeftRadius: "20px 20px",
|
|
106
|
+
}, 100, 100);
|
|
107
|
+
const d = roundedRectPath(0, 0, 100, 100, c);
|
|
108
|
+
const arcs = d.match(/A/g) || [];
|
|
109
|
+
expect(arcs.length).toBe(2);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
describe("roundedRectSvg: rect-or-path branching", () => {
|
|
113
|
+
it("emits <rect rx> for uniform circular corners (fast path)", () => {
|
|
114
|
+
const c = parseCornerRadii({
|
|
115
|
+
borderTopLeftRadius: "8px 8px",
|
|
116
|
+
borderTopRightRadius: "8px 8px",
|
|
117
|
+
borderBottomRightRadius: "8px 8px",
|
|
118
|
+
borderBottomLeftRadius: "8px 8px",
|
|
119
|
+
}, 100, 50);
|
|
120
|
+
const svg = roundedRectSvg(0, 0, 100, 50, c, 'fill="red"');
|
|
121
|
+
expect(svg.startsWith("<rect ")).toBe(true);
|
|
122
|
+
expect(svg).toContain('rx="8"');
|
|
123
|
+
expect(svg).toContain('fill="red"');
|
|
124
|
+
});
|
|
125
|
+
it("emits <path> for asymmetric corners (DM-300)", () => {
|
|
126
|
+
const c = parseCornerRadii({
|
|
127
|
+
borderTopLeftRadius: "10px 10px",
|
|
128
|
+
borderTopRightRadius: "30px 30px",
|
|
129
|
+
borderBottomRightRadius: "50px 50px",
|
|
130
|
+
borderBottomLeftRadius: "70px 70px",
|
|
131
|
+
}, 200, 100);
|
|
132
|
+
const svg = roundedRectSvg(0, 0, 200, 100, c, 'fill="blue"');
|
|
133
|
+
expect(svg.startsWith("<path ")).toBe(true);
|
|
134
|
+
expect(svg).toContain('fill="blue"');
|
|
135
|
+
// Four arcs.
|
|
136
|
+
expect((svg.match(/A/g) || []).length).toBe(4);
|
|
137
|
+
});
|
|
138
|
+
it("emits <path> for elliptical corners (50px / 20px)", () => {
|
|
139
|
+
const c = parseCornerRadii({
|
|
140
|
+
borderTopLeftRadius: "50px 20px",
|
|
141
|
+
borderTopRightRadius: "50px 20px",
|
|
142
|
+
borderBottomRightRadius: "50px 20px",
|
|
143
|
+
borderBottomLeftRadius: "50px 20px",
|
|
144
|
+
}, 400, 80);
|
|
145
|
+
const svg = roundedRectSvg(0, 0, 400, 80, c, "");
|
|
146
|
+
expect(svg.startsWith("<path ")).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Capture
|
|
3
|
+
*
|
|
4
|
+
* Uses Playwright to navigate to a URL, wait for it to settle,
|
|
5
|
+
* and capture the DOM as SVG via the dom-to-svg converter.
|
|
6
|
+
*/
|
|
7
|
+
import { type Browser, type LaunchOptions, type Page } from "@playwright/test";
|
|
8
|
+
export interface CaptureOptions {
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
mobile?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Sets the browser context's `prefers-color-scheme` media feature, which
|
|
14
|
+
* controls how dark-mode-aware sites resolve their CSS. Default behavior
|
|
15
|
+
* (undefined) follows Playwright's own default of "light".
|
|
16
|
+
*/
|
|
17
|
+
colorScheme?: "light" | "dark" | "no-preference";
|
|
18
|
+
/** Authenticate via dev-login API before capturing */
|
|
19
|
+
devUser?: string;
|
|
20
|
+
/**
|
|
21
|
+
* DM-512: when true, fetch every http(s) image URL referenced by the
|
|
22
|
+
* captured tree and inline it as a `data:` URI in the output SVG. The
|
|
23
|
+
* resulting SVG loads correctly in image viewers (Preview, QuickLook,
|
|
24
|
+
* Finder thumbnail, etc.) that don't fetch remote resources from local
|
|
25
|
+
* files. Adds capture-time network I/O proportional to the number of
|
|
26
|
+
* unique referenced URLs; per-URL fetch failures are logged via the
|
|
27
|
+
* capture-warnings pipeline but don't fail the overall capture.
|
|
28
|
+
* Default: false (URLs pass through verbatim — works in browsers that
|
|
29
|
+
* fetch from file:// pages but not in offline viewers).
|
|
30
|
+
*/
|
|
31
|
+
selfContained?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* DM-528: per-URL fetch timeout (ms) for the `selfContained` pre-pass.
|
|
34
|
+
* Caps the time a stalled CDN host can hold up the capture; total
|
|
35
|
+
* pre-pass time is bounded by `embedRemoteImagesTimeoutMs` (fetches run
|
|
36
|
+
* in parallel) rather than the sum across all URLs. Default 10000.
|
|
37
|
+
*/
|
|
38
|
+
embedRemoteImagesTimeoutMs?: number;
|
|
39
|
+
/**
|
|
40
|
+
* DM-529: number of retry attempts for transient failures (5xx /
|
|
41
|
+
* network-error / timeout) in the `selfContained` pre-pass. 4xx
|
|
42
|
+
* responses are not retried. Default 1.
|
|
43
|
+
*/
|
|
44
|
+
embedRemoteImagesRetries?: number;
|
|
45
|
+
/**
|
|
46
|
+
* DM-529: backoff delay (ms) between retry attempts in the
|
|
47
|
+
* `selfContained` pre-pass. Default 500.
|
|
48
|
+
*/
|
|
49
|
+
embedRemoteImagesRetryBackoffMs?: number;
|
|
50
|
+
/**
|
|
51
|
+
* DM-526 / DM-539: when true, run the `resizeEmbeddedImages` pre-pass
|
|
52
|
+
* after `embedRemoteImages` to downscale each inlined image to its
|
|
53
|
+
* consumer's render rect × `embedRemoteImagesHiDPIFactor`, re-encoded as
|
|
54
|
+
* PNG. Yields 50–80 % SVG size reduction on news-site captures with no
|
|
55
|
+
* visible diff at the captured viewport. No-op unless `selfContained` is
|
|
56
|
+
* also true (resize only acts on what the embed pass already inlined).
|
|
57
|
+
* Default false. See `docs/27-image-resize-on-embed.md`.
|
|
58
|
+
*/
|
|
59
|
+
embedRemoteImagesResize?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* DM-526 / DM-539: hiDPI multiplier applied to each consumer's render rect
|
|
62
|
+
* before resizing. `2.0` (default) leaves headroom for retina viewing /
|
|
63
|
+
* zoom; `1.0` produces the smallest output (matches Chromium's painted
|
|
64
|
+
* resolution at devicePixelRatio: 1); `3.0` covers iPhone-Pro density.
|
|
65
|
+
* Values < 1 are clamped to 1.
|
|
66
|
+
*/
|
|
67
|
+
embedRemoteImagesHiDPIFactor?: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Launch Chromium via Playwright, auto-installing the browser binary on first
|
|
71
|
+
* use if it's missing. Use this instead of importing `chromium` from
|
|
72
|
+
* `@playwright/test` directly when you want a frictionless first-run
|
|
73
|
+
* experience for users of your tool.
|
|
74
|
+
*
|
|
75
|
+
* The install step is `npx playwright install chromium` and runs synchronously
|
|
76
|
+
* (stdout / stderr inherited) so the user sees its progress. Subsequent calls
|
|
77
|
+
* are a normal `chromium.launch()` with no overhead.
|
|
78
|
+
*/
|
|
79
|
+
export declare function launchChromium(opts?: LaunchOptions): Promise<Browser>;
|
|
80
|
+
export declare class DemoRecorder {
|
|
81
|
+
private browser;
|
|
82
|
+
private context;
|
|
83
|
+
private page;
|
|
84
|
+
private width;
|
|
85
|
+
private height;
|
|
86
|
+
private baseUrl;
|
|
87
|
+
private selfContained;
|
|
88
|
+
private embedRemoteImagesTimeoutMs;
|
|
89
|
+
private embedRemoteImagesRetries;
|
|
90
|
+
private embedRemoteImagesRetryBackoffMs;
|
|
91
|
+
private embedRemoteImagesResize;
|
|
92
|
+
private embedRemoteImagesHiDPIFactor;
|
|
93
|
+
constructor(baseUrl: string, opts: CaptureOptions);
|
|
94
|
+
init(opts: CaptureOptions): Promise<void>;
|
|
95
|
+
/** Navigate to a URL and capture the visible DOM as SVG. */
|
|
96
|
+
captureUrl(path: string, waitMs?: number, idPrefix?: string): Promise<string>;
|
|
97
|
+
/** Capture the current page state as SVG content. */
|
|
98
|
+
captureCurrent(idPrefix?: string): Promise<string>;
|
|
99
|
+
/**
|
|
100
|
+
* Capture a full-page (scrollable) DOM as SVG.
|
|
101
|
+
* Returns SVG content that may be taller than the viewport.
|
|
102
|
+
*/
|
|
103
|
+
captureFullPage(idPrefix?: string): Promise<{
|
|
104
|
+
svgContent: string;
|
|
105
|
+
pageHeight: number;
|
|
106
|
+
}>;
|
|
107
|
+
/** Get the underlying Playwright page for custom interactions. */
|
|
108
|
+
getPage(): Page;
|
|
109
|
+
/** Get the bounding box of an element (for positioning overlays). */
|
|
110
|
+
getBoundingBox(selector: string): Promise<{
|
|
111
|
+
x: number;
|
|
112
|
+
y: number;
|
|
113
|
+
width: number;
|
|
114
|
+
height: number;
|
|
115
|
+
} | null>;
|
|
116
|
+
close(): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Install a `requestfinished` listener that records every font-file URL the
|
|
120
|
+
* browser fetches into a Set. Returns the set + a detach handle. Pair with
|
|
121
|
+
* `discoverAndRegisterWebfonts(page, tracker.urls)` after the page loads.
|
|
122
|
+
*
|
|
123
|
+
* Needed because `performance.getEntriesByType("resource")` omits
|
|
124
|
+
* cross-origin fonts that don't send `Timing-Allow-Origin: *` (most CDNs
|
|
125
|
+
* don't), and most webfonts in the wild are served cross-origin.
|
|
126
|
+
*
|
|
127
|
+
* Attach BEFORE navigation so the listener catches the initial fetches.
|
|
128
|
+
*/
|
|
129
|
+
export declare function attachWebfontTracker(page: Page): {
|
|
130
|
+
urls: Set<string>;
|
|
131
|
+
detach: () => void;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Discover all `@font-face` rules in the page's stylesheets, fetch each
|
|
135
|
+
* font file via the browser context's request API (so cookies / CORS / auth
|
|
136
|
+
* follow whatever the browser is using), and register the bytes with
|
|
137
|
+
* `text-to-path.ts` so the renderer can draw with the actual webfont glyphs
|
|
138
|
+
* instead of falling through to the system-font substitutes.
|
|
139
|
+
*
|
|
140
|
+
* Should be called AFTER `await page.evaluate(() => document.fonts.ready)`
|
|
141
|
+
* — otherwise late-loading fonts may not be in `document.styleSheets` yet.
|
|
142
|
+
*
|
|
143
|
+
* Cross-origin stylesheets whose `cssRules` throw a SecurityError are silently
|
|
144
|
+
* skipped (we can't enumerate their rules from JS). Same-origin sheets and
|
|
145
|
+
* inline `<style>` blocks always work.
|
|
146
|
+
*
|
|
147
|
+
* Caller is responsible for `clearWebfonts()` between captures if needed.
|
|
148
|
+
* No-op when the page declares no `@font-face` rules.
|
|
149
|
+
*/
|
|
150
|
+
export declare function discoverAndRegisterWebfonts(page: Page, observedFontUrls?: Iterable<string>): Promise<{
|
|
151
|
+
family: string;
|
|
152
|
+
weight: number;
|
|
153
|
+
style: string;
|
|
154
|
+
url: string;
|
|
155
|
+
source: "font-face" | "resource";
|
|
156
|
+
ok: boolean;
|
|
157
|
+
error?: string;
|
|
158
|
+
}[]>;
|
|
159
|
+
/**
|
|
160
|
+
* DM-545: parse `@font-face` rules out of a raw CSS text fetched server-side.
|
|
161
|
+
* Used for cross-origin stylesheets that the page-side `cssRules` walker can't
|
|
162
|
+
* read. Tolerant scanner — handles top-level `@font-face` and rules nested
|
|
163
|
+
* inside any number of `@media` / `@supports` / `@layer` / `@container`
|
|
164
|
+
* blocks (recurses through balanced braces). Returns the same FaceRule shape
|
|
165
|
+
* the page-side walker emits so the downstream registration loop is uniform.
|
|
166
|
+
*
|
|
167
|
+
* Not a full CSS parser — comment-stripping handles `/* … */`, but exotic
|
|
168
|
+
* inputs (custom properties holding @font-face strings, CSSOM-injected rules
|
|
169
|
+
* that never serialised back to text) aren't covered. Adequate for the
|
|
170
|
+
* mainstream marketing-site case that motivated the change.
|
|
171
|
+
*/
|
|
172
|
+
export declare function parseFontFaceRulesFromCssText(cssText: string, baseUrl: string): Array<{
|
|
173
|
+
kind: "font-face";
|
|
174
|
+
family: string;
|
|
175
|
+
weight: string;
|
|
176
|
+
style: string;
|
|
177
|
+
url: string;
|
|
178
|
+
urls?: string[];
|
|
179
|
+
unicodeRange?: Array<[number, number]>;
|
|
180
|
+
}>;
|
|
181
|
+
/**
|
|
182
|
+
* Parse a CSS `unicode-range` descriptor value (CSS Fonts 4 §4.5) into a list
|
|
183
|
+
* of inclusive `[from, to]` codepoint intervals. Accepts the three forms:
|
|
184
|
+
* single (`U+26`), interval (`U+0-7F`), and wildcard (`U+4??`). Multiple
|
|
185
|
+
* ranges are comma-separated. Returns `undefined` for empty / unparseable
|
|
186
|
+
* input — callers treat that as the CSS default coverage (U+0..U+10FFFF).
|
|
187
|
+
*
|
|
188
|
+
* This is the Node-side twin of the in-page parser inlined inside
|
|
189
|
+
* `discoverAndRegisterWebfonts`'s `page.evaluate` body. The page-side copy
|
|
190
|
+
* can't import from here (it runs in the browser context), but the logic must
|
|
191
|
+
* stay aligned — covered by tests on this exported version.
|
|
192
|
+
*/
|
|
193
|
+
export declare function parseUnicodeRangeDescriptor(value: string): Array<[number, number]> | undefined;
|