-
Notifications
You must be signed in to change notification settings - Fork 49
/
dom-to-foreign-object-svg.ts
102 lines (89 loc) · 2.97 KB
/
dom-to-foreign-object-svg.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import type { Context } from '../context'
import type { Options } from '../options'
import { cloneNode } from '../clone-node'
import { orCreateContext } from '../create-context'
import { destroyContext } from '../destroy-context'
import { embedNode } from '../embed-node'
import { embedWebFont } from '../embed-web-font'
import {
createSvg,
isElementNode,
isSVGElementNode,
} from '../utils'
export async function domToForeignObjectSvg<T extends Node>(node: T, options?: Options): Promise<SVGElement>
export async function domToForeignObjectSvg<T extends Node>(context: Context<T>): Promise<SVGElement>
export async function domToForeignObjectSvg(node: any, options?: any): Promise<SVGElement> {
const context = await orCreateContext(node, options)
if (isElementNode(context.node) && isSVGElementNode(context.node))
return context.node
const {
ownerDocument,
log,
tasks,
svgStyleElement,
svgDefsElement,
svgStyles,
font,
progress,
autoDestruct,
onCloneNode,
onEmbedNode,
onCreateForeignObjectSvg,
} = context
log.time('clone node')
const clone = await cloneNode(context.node, context, true)
if (svgStyleElement && ownerDocument) {
let allCssText = ''
svgStyles.forEach((klasses, cssText) => {
allCssText += `${klasses.join(',\n')} {\n ${cssText}\n}\n`
})
svgStyleElement.appendChild(ownerDocument.createTextNode(allCssText))
}
log.timeEnd('clone node')
await onCloneNode?.(clone)
if (font !== false && isElementNode(clone)) {
log.time('embed web font')
await embedWebFont(clone, context)
log.timeEnd('embed web font')
}
log.time('embed node')
embedNode(clone, context)
const count = tasks.length
let current = 0
const runTask = async (): Promise<void> => {
while (true) {
const task = tasks.pop()
if (!task)
break
try {
await task
}
catch (error) {
context.log.warn('Failed to run task', error)
}
progress?.(++current, count)
}
}
progress?.(current, count)
await Promise.all([...Array.from({ length: 4 })].map(runTask))
log.timeEnd('embed node')
await onEmbedNode?.(clone)
const svg = createForeignObjectSvg(clone, context)
svgDefsElement && svg.insertBefore(svgDefsElement, svg.children[0])
svgStyleElement && svg.insertBefore(svgStyleElement, svg.children[0])
autoDestruct && destroyContext(context)
await onCreateForeignObjectSvg?.(svg)
return svg
}
function createForeignObjectSvg(clone: Node, context: Context): SVGSVGElement {
const { width, height } = context
const svg = createSvg(width, height, clone.ownerDocument)
const foreignObject = svg.ownerDocument.createElementNS(svg.namespaceURI, 'foreignObject')
foreignObject.setAttributeNS(null, 'x', '0%')
foreignObject.setAttributeNS(null, 'y', '0%')
foreignObject.setAttributeNS(null, 'width', '100%')
foreignObject.setAttributeNS(null, 'height', '100%')
foreignObject.append(clone)
svg.appendChild(foreignObject)
return svg
}