Skip to content

Commit

Permalink
feat: add option to resolve problems which caused by html that has la…
Browse files Browse the repository at this point in the history
…rge amount of sub nodes
  • Loading branch information
hzsrc committed Aug 16, 2024
1 parent 128dc3e commit d5c17e1
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 7 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,15 @@ A string indicating the image format. The default type is image/png; that type i
When supplied, the toCanvas function will return a blob matching the given image type and quality.

Defaults to `image/png`


### usePageCss

Use `true` to add a `<style>` tag in svg content which imports all styles of current html page, and do not add computed styles to every node any more(this make svg content so large that Firefox throw errors while load svg as image).
This will make the svg content much smaller, to resolve problems which caused by html that has large amount of sub nodes.


Defaults to `false`

## Browsers

Expand Down
12 changes: 6 additions & 6 deletions src/clone-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ function cloneCSSStyle<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
) {
value = 'block'
}

if (name === 'd' && clonedNode.getAttribute('d')) {
value = `path(${clonedNode.getAttribute('d')})`
}

targetStyle.setProperty(
name,
value,
Expand Down Expand Up @@ -170,10 +170,10 @@ function cloneSelectValue<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
}
}

function decorate<T extends HTMLElement>(nativeNode: T, clonedNode: T): T {
function decorate<T extends HTMLElement>(nativeNode: T, clonedNode: T, usePageCss?: boolean): T {
if (isInstanceOfElement(clonedNode, Element)) {
cloneCSSStyle(nativeNode, clonedNode)
clonePseudoElements(nativeNode, clonedNode)
if (!usePageCss) cloneCSSStyle(nativeNode, clonedNode)
if (!usePageCss) clonePseudoElements(nativeNode, clonedNode)
cloneInputValue(nativeNode, clonedNode)
cloneSelectValue(nativeNode, clonedNode)
}
Expand Down Expand Up @@ -240,6 +240,6 @@ export async function cloneNode<T extends HTMLElement>(
return Promise.resolve(node)
.then((clonedNode) => cloneSingleNode(clonedNode, options) as Promise<T>)
.then((clonedNode) => cloneChildren(node, clonedNode, options))
.then((clonedNode) => decorate(node, clonedNode))
.then((clonedNode) => decorate(node, clonedNode, options.usePageCss))
.then((clonedNode) => ensureSVGSymbols(clonedNode, options))
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export async function toSvg<T extends HTMLElement>(
await embedWebFonts(clonedNode, options)
await embedImages(clonedNode, options)
applyStyle(clonedNode, options)
const datauri = await nodeToDataURL(clonedNode, width, height)
const datauri = await nodeToDataURL(clonedNode, width, height, options.usePageCss)
return datauri
}

Expand Down
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,9 @@ export interface Options {
*
*/
fetchRequestInit?: RequestInit
/*
* Use a <style> in svg to import all styles of current html page, and do not add computed styles to every node any more.
* This will make the svg content very small, to resolve problems when html has large amount of sub nodes.
* */
usePageCss?: boolean
}
27 changes: 27 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export async function nodeToDataURL(
node: HTMLElement,
width: number,
height: number,
usePageCss?: boolean,
): Promise<string> {
const xmlns = 'http://www.w3.org/2000/svg'
const svg = document.createElementNS(xmlns, 'svg')
Expand All @@ -220,6 +221,11 @@ export async function nodeToDataURL(

svg.appendChild(foreignObject)
foreignObject.appendChild(node)
if (usePageCss) {
const style = document.createElementNS(xmlns, 'style')
style.innerHTML = await getStyles()
svg.insertBefore(style, foreignObject)
}
return svgToDataURL(svg)
}

Expand All @@ -240,3 +246,24 @@ export const isInstanceOfElement = <
isInstanceOfElement(nodePrototype, instance)
)
}

export function getStyles() {
const styles = document.querySelectorAll('style,link[rel="stylesheet"]')
const ps: Array<Promise<string>> = []
toArray(styles).forEach((el) => {
const e = el as Element
if (e.tagName === 'LINK') {
const href = e.getAttribute('href')
if (href) ps.push(getCssText(href).catch(() => ''))
} else {
ps.push(Promise.resolve(e.innerHTML))
}
})
return Promise.all(ps).then((arr) => {
return arr.join('\n\n')
})

function getCssText(url: string) {
return fetch(url).then((r) => r.text())
}
}

0 comments on commit d5c17e1

Please sign in to comment.