@eturnity/dom_to_svg 8.10.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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/lib/accessibility.d.ts +3 -0
  4. package/lib/accessibility.d.ts.map +1 -0
  5. package/lib/accessibility.js +201 -0
  6. package/lib/accessibility.js.map +1 -0
  7. package/lib/css.d.ts +26 -0
  8. package/lib/css.d.ts.map +1 -0
  9. package/lib/css.js +96 -0
  10. package/lib/css.js.map +1 -0
  11. package/lib/dom.d.ts +22 -0
  12. package/lib/dom.d.ts.map +1 -0
  13. package/lib/dom.js +33 -0
  14. package/lib/dom.js.map +1 -0
  15. package/lib/element.d.ts +3 -0
  16. package/lib/element.d.ts.map +1 -0
  17. package/lib/element.js +417 -0
  18. package/lib/element.js.map +1 -0
  19. package/lib/gradients.d.ts +3 -0
  20. package/lib/gradients.d.ts.map +1 -0
  21. package/lib/gradients.js +78 -0
  22. package/lib/gradients.js.map +1 -0
  23. package/lib/index.d.ts +6 -0
  24. package/lib/index.d.ts.map +1 -0
  25. package/lib/index.js +75 -0
  26. package/lib/index.js.map +1 -0
  27. package/lib/inline.d.ts +14 -0
  28. package/lib/inline.d.ts.map +1 -0
  29. package/lib/inline.js +138 -0
  30. package/lib/inline.js.map +1 -0
  31. package/lib/stacking.d.ts +38 -0
  32. package/lib/stacking.d.ts.map +1 -0
  33. package/lib/stacking.js +125 -0
  34. package/lib/stacking.js.map +1 -0
  35. package/lib/svg.d.ts +14 -0
  36. package/lib/svg.d.ts.map +1 -0
  37. package/lib/svg.js +245 -0
  38. package/lib/svg.js.map +1 -0
  39. package/lib/test/PuppeteerAdapter.d.ts +90 -0
  40. package/lib/test/PuppeteerAdapter.d.ts.map +1 -0
  41. package/lib/test/PuppeteerAdapter.js +196 -0
  42. package/lib/test/PuppeteerAdapter.js.map +1 -0
  43. package/lib/test/injected-script.d.ts +2 -0
  44. package/lib/test/injected-script.d.ts.map +1 -0
  45. package/lib/test/injected-script.js +26 -0
  46. package/lib/test/injected-script.js.map +1 -0
  47. package/lib/test/test.d.ts +6 -0
  48. package/lib/test/test.d.ts.map +1 -0
  49. package/lib/test/test.js +245 -0
  50. package/lib/test/test.js.map +1 -0
  51. package/lib/test/util.d.ts +9 -0
  52. package/lib/test/util.d.ts.map +1 -0
  53. package/lib/test/util.js +33 -0
  54. package/lib/test/util.js.map +1 -0
  55. package/lib/text.d.ts +5 -0
  56. package/lib/text.d.ts.map +1 -0
  57. package/lib/text.js +138 -0
  58. package/lib/text.js.map +1 -0
  59. package/lib/traversal.d.ts +32 -0
  60. package/lib/traversal.d.ts.map +1 -0
  61. package/lib/traversal.js +12 -0
  62. package/lib/traversal.js.map +1 -0
  63. package/lib/util.d.ts +20 -0
  64. package/lib/util.d.ts.map +1 -0
  65. package/lib/util.js +43 -0
  66. package/lib/util.js.map +1 -0
  67. package/package.json +112 -0
package/lib/index.js ADDED
@@ -0,0 +1,75 @@
1
+ import * as postcss from 'postcss';
2
+ import cssValueParser from 'postcss-value-parser';
3
+ import { isCSSFontFaceRule, unescapeStringValue } from './css.js';
4
+ import { svgNamespace, xlinkNamespace } from './dom.js';
5
+ import { createStackingLayers } from './stacking.js';
6
+ import { walkNode } from './traversal.js';
7
+ import { createIdGenerator } from './util.js';
8
+ export function documentToSVG(document, options) {
9
+ return elementToSVG(document.documentElement, options);
10
+ }
11
+ export function elementToSVG(element, options) {
12
+ var _a, _b, _c, _d;
13
+ const svgDocument = element.ownerDocument.implementation.createDocument(svgNamespace, 'svg', null);
14
+ const svgElement = svgDocument.documentElement;
15
+ svgElement.setAttribute('xmlns', svgNamespace);
16
+ svgElement.setAttribute('xmlns:xlink', xlinkNamespace);
17
+ svgElement.append(svgDocument.createComment(
18
+ // "--" is invalid in comments, percent-encode.
19
+ ` Generated by dom-to-svg from ${element.ownerDocument.location.href.replace(/--/g, '%2D%2D')} `));
20
+ // Copy @font-face rules
21
+ const styleElement = svgDocument.createElementNS(svgNamespace, 'style');
22
+ for (const styleSheet of element.ownerDocument.styleSheets) {
23
+ try {
24
+ // Make font URLs absolute (need to be resolved relative to the stylesheet)
25
+ for (const rule of (_a = styleSheet.rules) !== null && _a !== void 0 ? _a : []) {
26
+ if (!isCSSFontFaceRule(rule)) {
27
+ continue;
28
+ }
29
+ const styleSheetHref = (_b = rule.parentStyleSheet) === null || _b === void 0 ? void 0 : _b.href;
30
+ if (styleSheetHref) {
31
+ // Note: Firefox does not implement rule.style.src, need to use rule.style.getPropertyValue()
32
+ const parsedSourceValue = cssValueParser(rule.style.getPropertyValue('src'));
33
+ parsedSourceValue.walk(node => {
34
+ if (node.type === 'function' && node.value === 'url' && node.nodes[0]) {
35
+ const urlArgumentNode = node.nodes[0];
36
+ if (urlArgumentNode.type === 'string' || urlArgumentNode.type === 'word') {
37
+ urlArgumentNode.value = new URL(unescapeStringValue(urlArgumentNode.value), styleSheetHref).href;
38
+ }
39
+ }
40
+ });
41
+ // Firefox does not support changing `src` on CSSFontFaceRule declarations, need to use PostCSS.
42
+ const updatedFontFaceRule = postcss.parse(rule.cssText);
43
+ updatedFontFaceRule.walkDecls('src', declaration => {
44
+ declaration.value = cssValueParser.stringify(parsedSourceValue.nodes);
45
+ });
46
+ styleElement.append(updatedFontFaceRule.toString() + '\n');
47
+ }
48
+ }
49
+ }
50
+ catch (error) {
51
+ console.error('Error resolving @font-face src URLs for styleSheet, skipping', styleSheet, error);
52
+ }
53
+ }
54
+ svgElement.append(styleElement);
55
+ walkNode(element, {
56
+ svgDocument,
57
+ currentSvgParent: svgElement,
58
+ stackingLayers: createStackingLayers(svgElement),
59
+ parentStackingLayer: svgElement,
60
+ getUniqueId: createIdGenerator(),
61
+ labels: new Map(),
62
+ ancestorMasks: [],
63
+ options: {
64
+ captureArea: (_c = options === null || options === void 0 ? void 0 : options.captureArea) !== null && _c !== void 0 ? _c : element.getBoundingClientRect(),
65
+ keepLinks: (options === null || options === void 0 ? void 0 : options.keepLinks) !== false,
66
+ },
67
+ });
68
+ const bounds = (_d = options === null || options === void 0 ? void 0 : options.captureArea) !== null && _d !== void 0 ? _d : element.getBoundingClientRect();
69
+ svgElement.setAttribute('width', bounds.width.toString());
70
+ svgElement.setAttribute('height', bounds.height.toString());
71
+ svgElement.setAttribute('viewBox', `${bounds.x} ${bounds.y} ${bounds.width} ${bounds.height}`);
72
+ return svgDocument;
73
+ }
74
+ export { inlineResources } from './inline.js';
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as postcss from 'postcss'\nimport cssValueParser from 'postcss-value-parser'\n\nimport { isCSSFontFaceRule, unescapeStringValue } from './css.js'\nimport { svgNamespace, xlinkNamespace } from './dom.js'\nimport { createStackingLayers } from './stacking.js'\nimport { DomToSvgOptions, walkNode } from './traversal.js'\nimport { createIdGenerator } from './util.js'\n\nexport { DomToSvgOptions }\n\nexport function documentToSVG(document: Document, options?: DomToSvgOptions): XMLDocument {\n\treturn elementToSVG(document.documentElement, options)\n}\n\nexport function elementToSVG(element: Element, options?: DomToSvgOptions): XMLDocument {\n\tconst svgDocument = element.ownerDocument.implementation.createDocument(svgNamespace, 'svg', null)\n\n\tconst svgElement = (svgDocument.documentElement as unknown) as SVGSVGElement\n\tsvgElement.setAttribute('xmlns', svgNamespace)\n\tsvgElement.setAttribute('xmlns:xlink', xlinkNamespace)\n\tsvgElement.append(\n\t\tsvgDocument.createComment(\n\t\t\t// \"--\" is invalid in comments, percent-encode.\n\t\t\t` Generated by dom-to-svg from ${element.ownerDocument.location.href.replace(/--/g, '%2D%2D')} `\n\t\t)\n\t)\n\n\t// Copy @font-face rules\n\tconst styleElement = svgDocument.createElementNS(svgNamespace, 'style')\n\tfor (const styleSheet of element.ownerDocument.styleSheets) {\n\t\ttry {\n\t\t\t// Make font URLs absolute (need to be resolved relative to the stylesheet)\n\t\t\tfor (const rule of styleSheet.rules ?? []) {\n\t\t\t\tif (!isCSSFontFaceRule(rule)) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst styleSheetHref = rule.parentStyleSheet?.href\n\t\t\t\tif (styleSheetHref) {\n\t\t\t\t\t// Note: Firefox does not implement rule.style.src, need to use rule.style.getPropertyValue()\n\t\t\t\t\tconst parsedSourceValue = cssValueParser(rule.style.getPropertyValue('src'))\n\t\t\t\t\tparsedSourceValue.walk(node => {\n\t\t\t\t\t\tif (node.type === 'function' && node.value === 'url' && node.nodes[0]) {\n\t\t\t\t\t\t\tconst urlArgumentNode = node.nodes[0]\n\t\t\t\t\t\t\tif (urlArgumentNode.type === 'string' || urlArgumentNode.type === 'word') {\n\t\t\t\t\t\t\t\turlArgumentNode.value = new URL(\n\t\t\t\t\t\t\t\t\tunescapeStringValue(urlArgumentNode.value),\n\t\t\t\t\t\t\t\t\tstyleSheetHref\n\t\t\t\t\t\t\t\t).href\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t// Firefox does not support changing `src` on CSSFontFaceRule declarations, need to use PostCSS.\n\t\t\t\t\tconst updatedFontFaceRule = postcss.parse(rule.cssText)\n\t\t\t\t\tupdatedFontFaceRule.walkDecls('src', declaration => {\n\t\t\t\t\t\tdeclaration.value = cssValueParser.stringify(parsedSourceValue.nodes)\n\t\t\t\t\t})\n\t\t\t\t\tstyleElement.append(updatedFontFaceRule.toString() + '\\n')\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.error('Error resolving @font-face src URLs for styleSheet, skipping', styleSheet, error)\n\t\t}\n\t}\n\tsvgElement.append(styleElement)\n\n\twalkNode(element, {\n\t\tsvgDocument,\n\t\tcurrentSvgParent: svgElement,\n\t\tstackingLayers: createStackingLayers(svgElement),\n\t\tparentStackingLayer: svgElement,\n\t\tgetUniqueId: createIdGenerator(),\n\t\tlabels: new Map<HTMLLabelElement, string>(),\n\t\tancestorMasks: [],\n\t\toptions: {\n\t\t\tcaptureArea: options?.captureArea ?? element.getBoundingClientRect(),\n\t\t\tkeepLinks: options?.keepLinks !== false,\n\t\t},\n\t})\n\n\tconst bounds = options?.captureArea ?? element.getBoundingClientRect()\n\tsvgElement.setAttribute('width', bounds.width.toString())\n\tsvgElement.setAttribute('height', bounds.height.toString())\n\tsvgElement.setAttribute('viewBox', `${bounds.x} ${bounds.y} ${bounds.width} ${bounds.height}`)\n\n\treturn svgDocument\n}\n\nexport { inlineResources } from './inline.js'\n"]}
@@ -0,0 +1,14 @@
1
+ declare global {
2
+ interface SVGStyleElement extends LinkStyle {
3
+ }
4
+ }
5
+ /**
6
+ * Inlines all external resources of the given element, such as fonts and images.
7
+ *
8
+ * Fonts and binary images are inlined as Base64 data: URIs.
9
+ *
10
+ * Images that reference another SVG are inlined by inlining the embedded SVG into the output SVG.
11
+ * Note: The passed element needs to be attached to a document with a window (`defaultView`) for this so that `getComputedStyle()` can be used.
12
+ */
13
+ export declare function inlineResources(element: Element): Promise<void>;
14
+ //# sourceMappingURL=inline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"AAQA,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,eAAgB,SAAQ,SAAS;KAAG;CAC9C;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFrE"}
package/lib/inline.js ADDED
@@ -0,0 +1,138 @@
1
+ import * as postcss from 'postcss';
2
+ import cssValueParser from 'postcss-value-parser';
3
+ import { unescapeStringValue } from './css.js';
4
+ import { isSVGImageElement, isSVGStyleElement, svgNamespace } from './dom.js';
5
+ import { handleSvgNode } from './svg.js';
6
+ import { withTimeout, assert } from './util.js';
7
+ /**
8
+ * Inlines all external resources of the given element, such as fonts and images.
9
+ *
10
+ * Fonts and binary images are inlined as Base64 data: URIs.
11
+ *
12
+ * Images that reference another SVG are inlined by inlining the embedded SVG into the output SVG.
13
+ * Note: The passed element needs to be attached to a document with a window (`defaultView`) for this so that `getComputedStyle()` can be used.
14
+ */
15
+ export async function inlineResources(element) {
16
+ await Promise.all([
17
+ ...[...element.children].map(inlineResources),
18
+ (async () => {
19
+ var _a;
20
+ if (isSVGImageElement(element)) {
21
+ const blob = await withTimeout(10000, `Timeout fetching ${element.href.baseVal}`, () => fetchResource(element.href.baseVal));
22
+ if (blob.type === 'image/svg+xml') {
23
+ // If the image is an SVG, inline it into the output SVG.
24
+ // Some tools (e.g. Figma) do not support nested SVG.
25
+ assert(element.ownerDocument, 'Expected <image> element to have ownerDocument');
26
+ // Replace <image> with inline <svg>
27
+ const embeddedSvgDocument = new DOMParser().parseFromString(await blob.text(), 'image/svg+xml');
28
+ const svgRoot = embeddedSvgDocument.documentElement;
29
+ svgRoot.setAttribute('x', element.getAttribute('x'));
30
+ svgRoot.setAttribute('y', element.getAttribute('y'));
31
+ svgRoot.setAttribute('width', element.getAttribute('width'));
32
+ svgRoot.setAttribute('height', element.getAttribute('height'));
33
+ svgRoot.remove();
34
+ element.replaceWith(svgRoot);
35
+ try {
36
+ // Let handleSvgNode inline the <svg> into a simple <g>
37
+ const svgDocument = element.ownerDocument;
38
+ const mount = svgDocument.createElementNS(svgNamespace, 'g');
39
+ assert(element.id, '<image> element must have ID');
40
+ handleSvgNode(svgRoot, {
41
+ currentSvgParent: mount,
42
+ svgDocument,
43
+ idPrefix: `${element.id}-`,
44
+ options: {
45
+ // SVGs embedded through <img> are never interactive.
46
+ keepLinks: false,
47
+ captureArea: svgRoot.viewBox.baseVal,
48
+ },
49
+ });
50
+ // Replace the <svg> element with the <g>
51
+ mount.dataset.tag = 'img';
52
+ mount.setAttribute('role', 'img');
53
+ svgRoot.replaceWith(mount);
54
+ }
55
+ finally {
56
+ svgRoot.remove();
57
+ }
58
+ }
59
+ else {
60
+ // Inline binary images as base64 data: URL
61
+ const dataUrl = await blobToDataURL(blob);
62
+ element.dataset.src = element.href.baseVal;
63
+ element.setAttribute('xlink:href', dataUrl.href);
64
+ }
65
+ }
66
+ else if (isSVGStyleElement(element)) {
67
+ try {
68
+ const promises = [];
69
+ // Walk the stylesheet and replace @font-face src URLs with data URIs
70
+ const parsedSheet = postcss.parse((_a = element.textContent) !== null && _a !== void 0 ? _a : '');
71
+ parsedSheet.walkAtRules('font-face', fontFaceRule => {
72
+ fontFaceRule.walkDecls('src', sourceDeclaration => {
73
+ const parsedSourceValue = cssValueParser(sourceDeclaration.value);
74
+ parsedSourceValue.walk(node => {
75
+ if (node.type === 'function' && node.value === 'url' && node.nodes[0]) {
76
+ const urlArgumentNode = node.nodes[0];
77
+ if (urlArgumentNode.type === 'string' || urlArgumentNode.type === 'word') {
78
+ promises.push(inlineCssFontUrlArgumentNode(urlArgumentNode));
79
+ }
80
+ }
81
+ });
82
+ sourceDeclaration.value = cssValueParser.stringify(parsedSourceValue.nodes);
83
+ });
84
+ });
85
+ await Promise.all(promises);
86
+ // Update <style> element with updated CSS
87
+ element.textContent = parsedSheet.toString();
88
+ }
89
+ catch (error) {
90
+ console.error('Error inlining stylesheet', element.sheet, error);
91
+ }
92
+ }
93
+ })().catch(error => {
94
+ console.error('Error inlining resource for element', element, error);
95
+ }),
96
+ ]);
97
+ }
98
+ /**
99
+ * Fetches the font from a `url()` CSS node and replaces it with a `data:` URI of the content.
100
+ */
101
+ async function inlineCssFontUrlArgumentNode(urlArgumentNode) {
102
+ try {
103
+ const url = new URL(unescapeStringValue(urlArgumentNode.value));
104
+ const blob = await withTimeout(10000, `Timeout fetching ${url.href}`, () => fetchResource(url.href));
105
+ if (!blob.type.startsWith('font/') &&
106
+ !blob.type.startsWith('application/font-') &&
107
+ !blob.type.startsWith('application/x-font-') &&
108
+ !blob.type.startsWith('image/svg+xml') &&
109
+ !blob.type.startsWith('application/vnd.ms-fontobject')) {
110
+ throw new Error(`Invalid response MIME type inlining font at ${url.href}: Expected font MIME type, got ${blob.type}`);
111
+ }
112
+ const dataUrl = await blobToDataURL(blob);
113
+ urlArgumentNode.value = dataUrl.href;
114
+ }
115
+ catch (error) {
116
+ console.error(`Error inlining ${urlArgumentNode.value}`, error);
117
+ }
118
+ }
119
+ async function fetchResource(url) {
120
+ assert(url, 'No URL passed');
121
+ const headers = new Headers();
122
+ const response = await fetch(url, { headers });
123
+ if (!response.ok) {
124
+ throw new Error(response.statusText);
125
+ }
126
+ const blob = await response.blob();
127
+ return blob;
128
+ }
129
+ async function blobToDataURL(blob) {
130
+ const reader = new FileReader();
131
+ await new Promise((resolve, reject) => {
132
+ reader.addEventListener('error', () => reject(new Error('Error loading resource with FileLoader')));
133
+ reader.addEventListener('load', () => resolve());
134
+ reader.readAsDataURL(blob);
135
+ });
136
+ return new URL(reader.result);
137
+ }
138
+ //# sourceMappingURL=inline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline.js","sourceRoot":"","sources":["../src/inline.ts"],"names":[],"mappings":"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","sourcesContent":["import * as postcss from 'postcss'\nimport cssValueParser from 'postcss-value-parser'\n\nimport { unescapeStringValue } from './css.js'\nimport { isSVGImageElement, isSVGStyleElement, svgNamespace } from './dom.js'\nimport { handleSvgNode } from './svg.js'\nimport { withTimeout, assert } from './util.js'\n\ndeclare global {\n\tinterface SVGStyleElement extends LinkStyle {}\n}\n\n/**\n * Inlines all external resources of the given element, such as fonts and images.\n *\n * Fonts and binary images are inlined as Base64 data: URIs.\n *\n * Images that reference another SVG are inlined by inlining the embedded SVG into the output SVG.\n * Note: The passed element needs to be attached to a document with a window (`defaultView`) for this so that `getComputedStyle()` can be used.\n */\nexport async function inlineResources(element: Element): Promise<void> {\n\tawait Promise.all([\n\t\t...[...element.children].map(inlineResources),\n\t\t(async () => {\n\t\t\tif (isSVGImageElement(element)) {\n\t\t\t\tconst blob = await withTimeout(10000, `Timeout fetching ${element.href.baseVal}`, () =>\n\t\t\t\t\tfetchResource(element.href.baseVal)\n\t\t\t\t)\n\t\t\t\tif (blob.type === 'image/svg+xml') {\n\t\t\t\t\t// If the image is an SVG, inline it into the output SVG.\n\t\t\t\t\t// Some tools (e.g. Figma) do not support nested SVG.\n\n\t\t\t\t\tassert(element.ownerDocument, 'Expected <image> element to have ownerDocument')\n\n\t\t\t\t\t// Replace <image> with inline <svg>\n\t\t\t\t\tconst embeddedSvgDocument = new DOMParser().parseFromString(\n\t\t\t\t\t\tawait blob.text(),\n\t\t\t\t\t\t'image/svg+xml'\n\t\t\t\t\t) as XMLDocument\n\t\t\t\t\tconst svgRoot = (embeddedSvgDocument.documentElement as Element) as SVGSVGElement\n\t\t\t\t\tsvgRoot.setAttribute('x', element.getAttribute('x')!)\n\t\t\t\t\tsvgRoot.setAttribute('y', element.getAttribute('y')!)\n\t\t\t\t\tsvgRoot.setAttribute('width', element.getAttribute('width')!)\n\t\t\t\t\tsvgRoot.setAttribute('height', element.getAttribute('height')!)\n\t\t\t\t\tsvgRoot.remove()\n\t\t\t\t\telement.replaceWith(svgRoot)\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Let handleSvgNode inline the <svg> into a simple <g>\n\t\t\t\t\t\tconst svgDocument = element.ownerDocument\n\t\t\t\t\t\tconst mount = svgDocument.createElementNS(svgNamespace, 'g')\n\t\t\t\t\t\tassert(element.id, '<image> element must have ID')\n\t\t\t\t\t\thandleSvgNode(svgRoot, {\n\t\t\t\t\t\t\tcurrentSvgParent: mount,\n\t\t\t\t\t\t\tsvgDocument,\n\t\t\t\t\t\t\tidPrefix: `${element.id}-`,\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\t// SVGs embedded through <img> are never interactive.\n\t\t\t\t\t\t\t\tkeepLinks: false,\n\t\t\t\t\t\t\t\tcaptureArea: svgRoot.viewBox.baseVal,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\t// Replace the <svg> element with the <g>\n\t\t\t\t\t\tmount.dataset.tag = 'img'\n\t\t\t\t\t\tmount.setAttribute('role', 'img')\n\t\t\t\t\t\tsvgRoot.replaceWith(mount)\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tsvgRoot.remove()\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Inline binary images as base64 data: URL\n\t\t\t\t\tconst dataUrl = await blobToDataURL(blob)\n\t\t\t\t\telement.dataset.src = element.href.baseVal\n\t\t\t\t\telement.setAttribute('xlink:href', dataUrl.href)\n\t\t\t\t}\n\t\t\t} else if (isSVGStyleElement(element)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst promises: Promise<void>[] = []\n\t\t\t\t\t// Walk the stylesheet and replace @font-face src URLs with data URIs\n\t\t\t\t\tconst parsedSheet = postcss.parse(element.textContent ?? '')\n\t\t\t\t\tparsedSheet.walkAtRules('font-face', fontFaceRule => {\n\t\t\t\t\t\tfontFaceRule.walkDecls('src', sourceDeclaration => {\n\t\t\t\t\t\t\tconst parsedSourceValue = cssValueParser(sourceDeclaration.value)\n\t\t\t\t\t\t\tparsedSourceValue.walk(node => {\n\t\t\t\t\t\t\t\tif (node.type === 'function' && node.value === 'url' && node.nodes[0]) {\n\t\t\t\t\t\t\t\t\tconst urlArgumentNode = node.nodes[0]\n\t\t\t\t\t\t\t\t\tif (urlArgumentNode.type === 'string' || urlArgumentNode.type === 'word') {\n\t\t\t\t\t\t\t\t\t\tpromises.push(inlineCssFontUrlArgumentNode(urlArgumentNode))\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tsourceDeclaration.value = cssValueParser.stringify(parsedSourceValue.nodes)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\tawait Promise.all(promises)\n\t\t\t\t\t// Update <style> element with updated CSS\n\t\t\t\t\telement.textContent = parsedSheet.toString()\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error('Error inlining stylesheet', element.sheet, error)\n\t\t\t\t}\n\t\t\t}\n\t\t})().catch(error => {\n\t\t\tconsole.error('Error inlining resource for element', element, error)\n\t\t}),\n\t])\n}\n\n/**\n * Fetches the font from a `url()` CSS node and replaces it with a `data:` URI of the content.\n */\nasync function inlineCssFontUrlArgumentNode(\n\turlArgumentNode: cssValueParser.StringNode | cssValueParser.WordNode\n): Promise<void> {\n\ttry {\n\t\tconst url = new URL(unescapeStringValue(urlArgumentNode.value))\n\t\tconst blob = await withTimeout(10000, `Timeout fetching ${url.href}`, () => fetchResource(url.href))\n\t\tif (\n\t\t\t!blob.type.startsWith('font/') &&\n\t\t\t!blob.type.startsWith('application/font-') &&\n\t\t\t!blob.type.startsWith('application/x-font-') &&\n\t\t\t!blob.type.startsWith('image/svg+xml') &&\n\t\t\t!blob.type.startsWith('application/vnd.ms-fontobject')\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid response MIME type inlining font at ${url.href}: Expected font MIME type, got ${blob.type}`\n\t\t\t)\n\t\t}\n\t\tconst dataUrl = await blobToDataURL(blob)\n\t\turlArgumentNode.value = dataUrl.href\n\t} catch (error) {\n\t\tconsole.error(`Error inlining ${urlArgumentNode.value}`, error)\n\t}\n}\n\nasync function fetchResource(url: string): Promise<Blob> {\n\tassert(url, 'No URL passed')\n\tconst headers = new Headers()\n\tconst response = await fetch(url, { headers })\n\tif (!response.ok) {\n\t\tthrow new Error(response.statusText)\n\t}\n\tconst blob = await response.blob()\n\treturn blob\n}\n\nasync function blobToDataURL(blob: Blob): Promise<URL> {\n\tconst reader = new FileReader()\n\tawait new Promise<void>((resolve, reject) => {\n\t\treader.addEventListener('error', () => reject(new Error('Error loading resource with FileLoader')))\n\t\treader.addEventListener('load', () => resolve())\n\t\treader.readAsDataURL(blob)\n\t})\n\treturn new URL(reader.result as string)\n}\n"]}
@@ -0,0 +1,38 @@
1
+ declare global {
2
+ interface CSSStyleDeclaration {
3
+ src: string;
4
+ mixBlendMode: string;
5
+ maskBorder: string;
6
+ isolation: string;
7
+ webkitOverflowScrolling: string;
8
+ contain: string;
9
+ displayOutside: string;
10
+ displayInside: string;
11
+ }
12
+ }
13
+ export declare function establishesStackingContext(styles: CSSStyleDeclaration, parentStyles: CSSStyleDeclaration | null): boolean;
14
+ export interface StackingLayers {
15
+ /** 1. The background and borders of the element forming the stacking context. */
16
+ readonly rootBackgroundAndBorders: SVGGElement;
17
+ /** 2. The child stacking contexts with negative stack levels (most negative first). */
18
+ readonly childStackingContextsWithNegativeStackLevels: SVGGElement;
19
+ /** 3. The in-flow, non-inline-level, non-positioned descendants. */
20
+ readonly inFlowNonInlineNonPositionedDescendants: SVGGElement;
21
+ /** 4. The non-positioned floats. */
22
+ readonly nonPositionedFloats: SVGGElement;
23
+ /** 5. The in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks. */
24
+ readonly inFlowInlineLevelNonPositionedDescendants: SVGGElement;
25
+ /** 6. The child stacking contexts with stack level 0 and the positioned descendants with stack level 0. */
26
+ readonly childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero: SVGGElement;
27
+ /** 7. The child stacking contexts with positive stack levels (least positive first). */
28
+ readonly childStackingContextsWithPositiveStackLevels: SVGGElement;
29
+ }
30
+ export declare function createStackingLayers(container: SVGElement): StackingLayers;
31
+ export declare function determineStackingLayer(styles: CSSStyleDeclaration, parentStyles: CSSStyleDeclaration | null): keyof StackingLayers | undefined;
32
+ export declare function sortChildrenByZIndex(parent: SVGElement): void;
33
+ export declare function sortStackingLayerChildren(stackingLayers: StackingLayers): void;
34
+ /**
35
+ * Removes all stacking layers that are empty.
36
+ */
37
+ export declare function cleanupStackingLayerChildren(stackingLayers: StackingLayers): void;
38
+ //# sourceMappingURL=stacking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stacking.d.ts","sourceRoot":"","sources":["../src/stacking.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,mBAAmB;QAE5B,GAAG,EAAE,MAAM,CAAA;QAEX,YAAY,EAAE,MAAM,CAAA;QACpB,UAAU,EAAE,MAAM,CAAA;QAClB,SAAS,EAAE,MAAM,CAAA;QACjB,uBAAuB,EAAE,MAAM,CAAA;QAC/B,OAAO,EAAE,MAAM,CAAA;QACf,cAAc,EAAE,MAAM,CAAA;QACtB,aAAa,EAAE,MAAM,CAAA;KACrB;CACD;AAmBD,wBAAgB,0BAA0B,CACzC,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,mBAAmB,GAAG,IAAI,GACtC,OAAO,CA0BT;AAED,MAAM,WAAW,cAAc;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,wBAAwB,EAAE,WAAW,CAAA;IAE9C,uFAAuF;IACvF,QAAQ,CAAC,4CAA4C,EAAE,WAAW,CAAA;IAElE,oEAAoE;IACpE,QAAQ,CAAC,uCAAuC,EAAE,WAAW,CAAA;IAE7D,oCAAoC;IACpC,QAAQ,CAAC,mBAAmB,EAAE,WAAW,CAAA;IAEzC,2GAA2G;IAC3G,QAAQ,CAAC,yCAAyC,EAAE,WAAW,CAAA;IAE/D,2GAA2G;IAC3G,QAAQ,CAAC,iFAAiF,EAAE,WAAW,CAAA;IAEvG,wFAAwF;IACxF,QAAQ,CAAC,4CAA4C,EAAE,WAAW,CAAA;CAClE;AAmBD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,UAAU,GAAG,cAAc,CA0B1E;AAED,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,mBAAmB,GAAG,IAAI,GACtC,MAAM,cAAc,GAAG,SAAS,CAyBlC;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAa7D;AAED,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI,CAG9E;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,cAAc,EAAE,cAAc,GAAG,IAAI,CAOjF"}
@@ -0,0 +1,125 @@
1
+ import { isInFlow, isInline, isPositioned } from './css.js';
2
+ import { svgNamespace } from './dom.js';
3
+ const stackingContextEstablishingProperties = new Set([
4
+ 'clipPath',
5
+ 'contain',
6
+ 'filter',
7
+ 'isolation',
8
+ 'mask',
9
+ 'maskBorder',
10
+ 'maskImage',
11
+ 'mixBlendMode',
12
+ 'opacity',
13
+ 'perspective',
14
+ 'position',
15
+ 'transform',
16
+ 'webkitOverflowScrolling',
17
+ 'zIndex',
18
+ ]);
19
+ export function establishesStackingContext(styles, parentStyles) {
20
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
21
+ return !!(((styles.position === 'absolute' || styles.position === 'relative') && styles.zIndex !== 'auto') ||
22
+ styles.position === 'fixed' ||
23
+ styles.position === 'sticky' ||
24
+ (parentStyles &&
25
+ (parentStyles.display === 'flex' || parentStyles.display === 'grid') &&
26
+ styles.zIndex !== 'auto') ||
27
+ parseFloat(styles.opacity) !== 1 ||
28
+ styles.mixBlendMode !== 'normal' ||
29
+ styles.transform !== 'none' ||
30
+ styles.filter !== 'none' ||
31
+ styles.perspective !== 'none' ||
32
+ styles.clipPath !== 'none' ||
33
+ styles.mask !== 'none' ||
34
+ styles.maskImage !== 'none' ||
35
+ styles.maskBorder !== 'none' ||
36
+ styles.isolation === 'isolate' ||
37
+ styles.webkitOverflowScrolling === 'touch' ||
38
+ styles.contain === 'layout' ||
39
+ styles.contain === 'paint' ||
40
+ styles.contain === 'strict' ||
41
+ styles.contain === 'content' ||
42
+ styles.willChange.split(',').some(property => stackingContextEstablishingProperties.has(property.trim())));
43
+ }
44
+ const STACKING_LAYER_NAMES = [
45
+ 'rootBackgroundAndBorders',
46
+ 'childStackingContextsWithNegativeStackLevels',
47
+ 'inFlowNonInlineNonPositionedDescendants',
48
+ 'nonPositionedFloats',
49
+ 'inFlowInlineLevelNonPositionedDescendants',
50
+ 'childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero',
51
+ 'childStackingContextsWithPositiveStackLevels',
52
+ ];
53
+ function createStackingLayer(parent, layerName) {
54
+ const layer = parent.ownerDocument.createElementNS(svgNamespace, 'g');
55
+ layer.dataset.stackingLayer = layerName;
56
+ parent.append(layer);
57
+ return layer;
58
+ }
59
+ export function createStackingLayers(container) {
60
+ container.dataset.stackingContext = 'true';
61
+ return {
62
+ rootBackgroundAndBorders: createStackingLayer(container, 'rootBackgroundAndBorders'),
63
+ childStackingContextsWithNegativeStackLevels: createStackingLayer(container, 'childStackingContextsWithNegativeStackLevels'),
64
+ inFlowNonInlineNonPositionedDescendants: createStackingLayer(container, 'inFlowNonInlineNonPositionedDescendants'),
65
+ nonPositionedFloats: createStackingLayer(container, 'nonPositionedFloats'),
66
+ inFlowInlineLevelNonPositionedDescendants: createStackingLayer(container, 'inFlowInlineLevelNonPositionedDescendants'),
67
+ childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero: createStackingLayer(container, 'childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero'),
68
+ childStackingContextsWithPositiveStackLevels: createStackingLayer(container, 'childStackingContextsWithPositiveStackLevels'),
69
+ };
70
+ }
71
+ export function determineStackingLayer(styles, parentStyles) {
72
+ // https://www.w3.org/TR/CSS22/visuren.html#layers
73
+ // https://www.w3.org/TR/CSS22/zindex.html
74
+ // Note: the root element is not handled here, but in handleElement().
75
+ const zIndex = styles.zIndex !== 'auto' ? parseInt(styles.zIndex, 10) : undefined;
76
+ if (zIndex !== undefined && zIndex < 0 && establishesStackingContext(styles, parentStyles)) {
77
+ return 'childStackingContextsWithNegativeStackLevels';
78
+ }
79
+ if (isInFlow(styles) && !isInline(styles) && !isPositioned(styles)) {
80
+ return 'inFlowNonInlineNonPositionedDescendants';
81
+ }
82
+ if (!isPositioned(styles) && styles.float !== 'none') {
83
+ return 'nonPositionedFloats';
84
+ }
85
+ if (isInFlow(styles) && isInline(styles) && !isPositioned(styles)) {
86
+ return 'inFlowInlineLevelNonPositionedDescendants';
87
+ }
88
+ if (zIndex === 0 && (isPositioned(styles) || establishesStackingContext(styles, parentStyles))) {
89
+ return 'childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero';
90
+ }
91
+ if (zIndex !== undefined && zIndex > 0 && establishesStackingContext(styles, parentStyles)) {
92
+ return 'childStackingContextsWithPositiveStackLevels';
93
+ }
94
+ return undefined;
95
+ }
96
+ export function sortChildrenByZIndex(parent) {
97
+ const sorted = [...parent.children].sort((a, b) => {
98
+ const zIndexA = a.dataset.zIndex;
99
+ const zIndexB = b.dataset.zIndex;
100
+ if (!zIndexA || !zIndexB) {
101
+ // E.g. a <clipPath>
102
+ return 0;
103
+ }
104
+ return parseInt(zIndexA, 10) - parseInt(zIndexB, 10);
105
+ });
106
+ for (const child of sorted) {
107
+ parent.append(child);
108
+ }
109
+ }
110
+ export function sortStackingLayerChildren(stackingLayers) {
111
+ sortChildrenByZIndex(stackingLayers.childStackingContextsWithNegativeStackLevels);
112
+ sortChildrenByZIndex(stackingLayers.childStackingContextsWithPositiveStackLevels);
113
+ }
114
+ /**
115
+ * Removes all stacking layers that are empty.
116
+ */
117
+ export function cleanupStackingLayerChildren(stackingLayers) {
118
+ for (const name of STACKING_LAYER_NAMES) {
119
+ const layer = stackingLayers[name];
120
+ if (!layer.hasChildNodes()) {
121
+ layer.remove();
122
+ }
123
+ }
124
+ }
125
+ //# sourceMappingURL=stacking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stacking.js","sourceRoot":"","sources":["../src/stacking.ts"],"names":[],"mappings":"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","sourcesContent":["import { isInFlow, isInline, isPositioned } from './css.js'\nimport { svgNamespace } from './dom.js'\n\ndeclare global {\n\tinterface CSSStyleDeclaration {\n\t\t// @font-face\n\t\tsrc: string\n\n\t\tmixBlendMode: string\n\t\tmaskBorder: string\n\t\tisolation: string\n\t\twebkitOverflowScrolling: string\n\t\tcontain: string\n\t\tdisplayOutside: string\n\t\tdisplayInside: string\n\t}\n}\n\nconst stackingContextEstablishingProperties = new Set<string>([\n\t'clipPath',\n\t'contain',\n\t'filter',\n\t'isolation',\n\t'mask',\n\t'maskBorder',\n\t'maskImage',\n\t'mixBlendMode',\n\t'opacity',\n\t'perspective',\n\t'position',\n\t'transform',\n\t'webkitOverflowScrolling',\n\t'zIndex',\n])\n\nexport function establishesStackingContext(\n\tstyles: CSSStyleDeclaration,\n\tparentStyles: CSSStyleDeclaration | null\n): boolean {\n\t// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context\n\treturn !!(\n\t\t((styles.position === 'absolute' || styles.position === 'relative') && styles.zIndex !== 'auto') ||\n\t\tstyles.position === 'fixed' ||\n\t\tstyles.position === 'sticky' ||\n\t\t(parentStyles &&\n\t\t\t(parentStyles.display === 'flex' || parentStyles.display === 'grid') &&\n\t\t\tstyles.zIndex !== 'auto') ||\n\t\tparseFloat(styles.opacity) !== 1 ||\n\t\tstyles.mixBlendMode !== 'normal' ||\n\t\tstyles.transform !== 'none' ||\n\t\tstyles.filter !== 'none' ||\n\t\tstyles.perspective !== 'none' ||\n\t\tstyles.clipPath !== 'none' ||\n\t\tstyles.mask !== 'none' ||\n\t\tstyles.maskImage !== 'none' ||\n\t\tstyles.maskBorder !== 'none' ||\n\t\tstyles.isolation === 'isolate' ||\n\t\tstyles.webkitOverflowScrolling === 'touch' ||\n\t\tstyles.contain === 'layout' ||\n\t\tstyles.contain === 'paint' ||\n\t\tstyles.contain === 'strict' ||\n\t\tstyles.contain === 'content' ||\n\t\tstyles.willChange.split(',').some(property => stackingContextEstablishingProperties.has(property.trim()))\n\t)\n}\n\nexport interface StackingLayers {\n\t/** 1. The background and borders of the element forming the stacking context. */\n\treadonly rootBackgroundAndBorders: SVGGElement\n\n\t/** 2. The child stacking contexts with negative stack levels (most negative first). */\n\treadonly childStackingContextsWithNegativeStackLevels: SVGGElement\n\n\t/** 3. The in-flow, non-inline-level, non-positioned descendants. */\n\treadonly inFlowNonInlineNonPositionedDescendants: SVGGElement\n\n\t/** 4. The non-positioned floats. */\n\treadonly nonPositionedFloats: SVGGElement\n\n\t/** 5. The in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks. */\n\treadonly inFlowInlineLevelNonPositionedDescendants: SVGGElement\n\n\t/** 6. The child stacking contexts with stack level 0 and the positioned descendants with stack level 0. */\n\treadonly childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero: SVGGElement\n\n\t/** 7. The child stacking contexts with positive stack levels (least positive first). */\n\treadonly childStackingContextsWithPositiveStackLevels: SVGGElement\n}\n\nconst STACKING_LAYER_NAMES: readonly (keyof StackingLayers)[] = [\n\t'rootBackgroundAndBorders',\n\t'childStackingContextsWithNegativeStackLevels',\n\t'inFlowNonInlineNonPositionedDescendants',\n\t'nonPositionedFloats',\n\t'inFlowInlineLevelNonPositionedDescendants',\n\t'childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero',\n\t'childStackingContextsWithPositiveStackLevels',\n]\n\nfunction createStackingLayer(parent: SVGElement, layerName: keyof StackingLayers): SVGGElement {\n\tconst layer = parent.ownerDocument.createElementNS(svgNamespace, 'g')\n\tlayer.dataset.stackingLayer = layerName\n\tparent.append(layer)\n\treturn layer\n}\n\nexport function createStackingLayers(container: SVGElement): StackingLayers {\n\tcontainer.dataset.stackingContext = 'true'\n\treturn {\n\t\trootBackgroundAndBorders: createStackingLayer(container, 'rootBackgroundAndBorders'),\n\t\tchildStackingContextsWithNegativeStackLevels: createStackingLayer(\n\t\t\tcontainer,\n\t\t\t'childStackingContextsWithNegativeStackLevels'\n\t\t),\n\t\tinFlowNonInlineNonPositionedDescendants: createStackingLayer(\n\t\t\tcontainer,\n\t\t\t'inFlowNonInlineNonPositionedDescendants'\n\t\t),\n\t\tnonPositionedFloats: createStackingLayer(container, 'nonPositionedFloats'),\n\t\tinFlowInlineLevelNonPositionedDescendants: createStackingLayer(\n\t\t\tcontainer,\n\t\t\t'inFlowInlineLevelNonPositionedDescendants'\n\t\t),\n\t\tchildStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero: createStackingLayer(\n\t\t\tcontainer,\n\t\t\t'childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero'\n\t\t),\n\t\tchildStackingContextsWithPositiveStackLevels: createStackingLayer(\n\t\t\tcontainer,\n\t\t\t'childStackingContextsWithPositiveStackLevels'\n\t\t),\n\t}\n}\n\nexport function determineStackingLayer(\n\tstyles: CSSStyleDeclaration,\n\tparentStyles: CSSStyleDeclaration | null\n): keyof StackingLayers | undefined {\n\t// https://www.w3.org/TR/CSS22/visuren.html#layers\n\t// https://www.w3.org/TR/CSS22/zindex.html\n\n\t// Note: the root element is not handled here, but in handleElement().\n\tconst zIndex = styles.zIndex !== 'auto' ? parseInt(styles.zIndex, 10) : undefined\n\tif (zIndex !== undefined && zIndex < 0 && establishesStackingContext(styles, parentStyles)) {\n\t\treturn 'childStackingContextsWithNegativeStackLevels'\n\t}\n\tif (isInFlow(styles) && !isInline(styles) && !isPositioned(styles)) {\n\t\treturn 'inFlowNonInlineNonPositionedDescendants'\n\t}\n\tif (!isPositioned(styles) && styles.float !== 'none') {\n\t\treturn 'nonPositionedFloats'\n\t}\n\tif (isInFlow(styles) && isInline(styles) && !isPositioned(styles)) {\n\t\treturn 'inFlowInlineLevelNonPositionedDescendants'\n\t}\n\tif (zIndex === 0 && (isPositioned(styles) || establishesStackingContext(styles, parentStyles))) {\n\t\treturn 'childStackingContextsWithStackLevelZeroAndPositionedDescendantsWithStackLevelZero'\n\t}\n\tif (zIndex !== undefined && zIndex > 0 && establishesStackingContext(styles, parentStyles)) {\n\t\treturn 'childStackingContextsWithPositiveStackLevels'\n\t}\n\treturn undefined\n}\n\nexport function sortChildrenByZIndex(parent: SVGElement): void {\n\tconst sorted = [...parent.children].sort((a, b) => {\n\t\tconst zIndexA = (a as SVGElement).dataset.zIndex\n\t\tconst zIndexB = (b as SVGElement).dataset.zIndex\n\t\tif (!zIndexA || !zIndexB) {\n\t\t\t// E.g. a <clipPath>\n\t\t\treturn 0\n\t\t}\n\t\treturn parseInt(zIndexA, 10) - parseInt(zIndexB, 10)\n\t})\n\tfor (const child of sorted) {\n\t\tparent.append(child)\n\t}\n}\n\nexport function sortStackingLayerChildren(stackingLayers: StackingLayers): void {\n\tsortChildrenByZIndex(stackingLayers.childStackingContextsWithNegativeStackLevels)\n\tsortChildrenByZIndex(stackingLayers.childStackingContextsWithPositiveStackLevels)\n}\n\n/**\n * Removes all stacking layers that are empty.\n */\nexport function cleanupStackingLayerChildren(stackingLayers: StackingLayers): void {\n\tfor (const name of STACKING_LAYER_NAMES) {\n\t\tconst layer = stackingLayers[name]\n\t\tif (!layer.hasChildNodes()) {\n\t\t\tlayer.remove()\n\t\t}\n\t}\n}\n"]}
package/lib/svg.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { TraversalContext } from './traversal.js';
2
+ /**
3
+ * Recursively clone an `<svg>` element, inlining it into the output SVG document with the necessary transforms.
4
+ */
5
+ export declare function handleSvgNode(node: Node, context: SvgTraversalContext): void;
6
+ interface SvgTraversalContext extends Pick<TraversalContext, 'svgDocument' | 'currentSvgParent' | 'options'> {
7
+ /**
8
+ * A prefix to use for all ID to make them unique inside the output SVG document.
9
+ */
10
+ readonly idPrefix: string;
11
+ }
12
+ export declare function handleSvgElement(element: SVGElement, context: SvgTraversalContext): void;
13
+ export {};
14
+ //# sourceMappingURL=svg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"svg.d.ts","sourceRoot":"","sources":["../src/svg.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGjD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAU5E;AAID,UAAU,mBAAoB,SAAQ,IAAI,CAAC,gBAAgB,EAAE,aAAa,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAC3G;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CACzB;AAGD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAwHxF"}