From 322b223a1e09d0b6e8c22b7f62d59d18a0a3dfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=A0=E6=89=8B=E6=8D=A7=E9=B2=9C=E8=8A=B1?= <157215725@qq.com> Date: Wed, 26 Apr 2023 14:25:52 +0800 Subject: [PATCH] feat: support currentColor in svg.symbol --- index.html | 1 - src/clone-node.ts | 4 -- src/copy-css-styles.ts | 4 +- src/copy-pseudo-class.ts | 2 +- src/get-default-style.ts | 74 ++++++++++++++++++++++++++-------- src/get-diff-style.ts | 7 +--- test/fixtures/svg.color.html | 6 +-- test/fixtures/svg.symbol.html | 20 +++++++++ test/fixtures/svg.symbol.png | Bin 0 -> 11344 bytes 9 files changed, 85 insertions(+), 33 deletions(-) create mode 100644 test/fixtures/svg.symbol.html create mode 100644 test/fixtures/svg.symbol.png diff --git a/index.html b/index.html index f76ebb9..5d14a16 100644 --- a/index.html +++ b/index.html @@ -10,7 +10,6 @@ body, html { margin: 0; - color: white; } diff --git a/src/clone-node.ts b/src/clone-node.ts index 0a28c0f..f8ae30c 100644 --- a/src/clone-node.ts +++ b/src/clone-node.ts @@ -84,10 +84,6 @@ export async function cloneNode( && (isHTMLElementNode(node) || isSVGElementNode(node))) { const computedStyle = ownerWindow.getComputedStyle(node) - if (computedStyle.display === 'none') { - return ownerDocument.createComment(node.tagName.toLowerCase()) - } - const cloned = await cloneElement(node, context) const clonedStyle = cloned.style diff --git a/src/copy-css-styles.ts b/src/copy-css-styles.ts index cdfd1d0..553949e 100644 --- a/src/copy-css-styles.ts +++ b/src/copy-css-styles.ts @@ -17,8 +17,8 @@ export function copyCssStyles( context: Context, ) { const clonedStyle = cloned.style - const defaultStyle = getDefaultStyle(node.nodeName, null, context) - const diffStyle = getDiffStyle(computedStyle, defaultStyle, node) + const defaultStyle = getDefaultStyle(node, null, context) + const diffStyle = getDiffStyle(computedStyle, defaultStyle) for (const [name, [value, priority]] of Object.entries(diffStyle)) { if (ignoredStyle.includes(name)) continue diff --git a/src/copy-pseudo-class.ts b/src/copy-pseudo-class.ts index 7dff682..e34d8d2 100644 --- a/src/copy-pseudo-class.ts +++ b/src/copy-pseudo-class.ts @@ -43,7 +43,7 @@ export function copyPseudoClass( if (!content || content === 'none') return const klasses = [uuid()] - const defaultStyle = getDefaultStyle(node.nodeName, pseudoClass, context) + const defaultStyle = getDefaultStyle(node, pseudoClass, context) const cloneStyle = [ `content: '${ content.replace(/'|"/g, '') }';`, ] diff --git a/src/get-default-style.ts b/src/get-default-style.ts index 4790abe..76be746 100644 --- a/src/get-default-style.ts +++ b/src/get-default-style.ts @@ -1,11 +1,42 @@ -import { uuid } from './utils' +import { isSVGElementNode, uuid } from './utils' import type { Context } from './context' -export function getDefaultStyle(nodeName: string, pseudoElement: string | null, context: Context) { - nodeName = nodeName.toLowerCase() +const ignoredStyles = [ + 'width', + 'height', +] + +const includedAttributes = [ + 'stroke', + 'fill', +] + +export function getDefaultStyle( + node: HTMLElement | SVGElement, + pseudoElement: string | null, + context: Context, +) { const { defaultComputedStyles, ownerDocument } = context - const key = `${ nodeName }${ pseudoElement ?? '' }` + + const nodeName = node.nodeName.toLowerCase() + const isSvgNode = isSVGElementNode(node) && nodeName !== 'svg' + const attributes = isSvgNode + ? includedAttributes + .map(name => [name, node.getAttribute(name)]) + .filter(([, value]) => value !== null) + : [] + + const key = [ + isSvgNode && 'svg', + nodeName, + attributes.map((name, value) => `${ name }=${ value }`).join(','), + pseudoElement, + ] + .filter(Boolean) + .join(':') + if (defaultComputedStyles.has(key)) return defaultComputedStyles.get(key)! + let sandbox = context.sandbox if (!sandbox) { if (ownerDocument) { @@ -21,24 +52,35 @@ export function getDefaultStyle(nodeName: string, pseudoElement: string | null, } } if (!sandbox) return {} + const sandboxWindow = sandbox.contentWindow if (!sandboxWindow) return {} const sandboxDocument = sandboxWindow.document - const el = sandboxDocument.createElement(nodeName) - sandboxDocument.body.appendChild(el) - // Ensure that there is some content, so properties like margin are applied + + let root: HTMLElement | SVGSVGElement + let el: Element + if (isSvgNode) { + root = sandboxDocument.createElementNS('http://www.w3.org/2000/svg', 'svg') + el = root.ownerDocument.createElementNS(root.namespaceURI, nodeName) + attributes.forEach(([name, value]) => { + el.setAttributeNS(null, name!, value!) + }) + root.appendChild(el) + } else { + root = el = sandboxDocument.createElement(nodeName) + } el.textContent = ' ' - const style = sandboxWindow.getComputedStyle(el, pseudoElement) + sandboxDocument.body.appendChild(root) + const computedStyle = sandboxWindow.getComputedStyle(el, pseudoElement) const styles: Record = {} - for (let i = style.length - 1; i >= 0; i--) { - const name = style.item(i) - if (name === 'width' || name === 'height') { - styles[name] = 'auto' - } else { - styles[name] = style.getPropertyValue(name) - } + for (let len = computedStyle.length, i = 0; i < len; i++) { + const name = computedStyle.item(i) + if (ignoredStyles.includes(name)) continue + styles[name] = computedStyle.getPropertyValue(name) } - sandboxDocument.body.removeChild(el) + sandboxDocument.body.removeChild(root) + defaultComputedStyles.set(key, styles) + return styles } diff --git a/src/get-diff-style.ts b/src/get-diff-style.ts index bacdc70..0e0e6b3 100644 --- a/src/get-diff-style.ts +++ b/src/get-diff-style.ts @@ -6,7 +6,6 @@ const getPrefix = (name: string) => name export function getDiffStyle( style: CSSStyleDeclaration, defaultStyle: Record, - node?: HTMLElement | SVGElement, ) { const diffStyle: Record = {} const diffStylePrefixs: string[] = [] @@ -23,11 +22,7 @@ export function getDiffStyle( prefixTree[prefix][name] = [value, priority] } - if ( - defaultStyle[name] === value - && !priority - && (node && !node.getAttribute(name)) - ) continue + if (defaultStyle[name] === value && !priority) continue if (prefix) { diffStylePrefixs.push(prefix) diff --git a/test/fixtures/svg.color.html b/test/fixtures/svg.color.html index 1c5ea4d..6ae7994 100644 --- a/test/fixtures/svg.color.html +++ b/test/fixtures/svg.color.html @@ -25,9 +25,9 @@ height="120" requiredExtensions="http://www.w3.org/1999/xhtml" > - + diff --git a/test/fixtures/svg.symbol.html b/test/fixtures/svg.symbol.html new file mode 100644 index 0000000..de27480 --- /dev/null +++ b/test/fixtures/svg.symbol.html @@ -0,0 +1,20 @@ + diff --git a/test/fixtures/svg.symbol.png b/test/fixtures/svg.symbol.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4a8f0b1195cbdbeceba40b0374846cea21222f GIT binary patch literal 11344 zcmeHtXFyaRG;qCRb5ZjQx&G8rA$S^OaVa`hW?6DTm!K&>*n&bwf!b{;*M5NeMqZR_R8_ z&3p?{yhVo4OQP_{vcA5)1YA<%@jU9%L+OljQMeL03o&|nWN$ZR4Aqh?A*L89~d85okr zI{hGlbj6yPgee9MfLL+2m@7RJ20gWVYY*OoWyrn_{!FBR-~uijd|*fehHMJO#*C0c z*Ezc?J{=<m*iHVaqIHQTHOG zjFlY8V$tY2IxvtEI3K>Op_NnAv8aPE1P4RfQce^Yc*)j%vz&>=Of0H3V!t=0!V(tR z-26LdBK^kN9>c)$RYLaA%bu@-5ONbkUj#wAWms_+Z9|6V?6q&+Cw)S7#HD?^?&zrmG=+nE$ydSvtT zDUmTa=h8f-G|5vfMQc#B8FCwY*vkZ^<_G;yBc-H#r1-Q*)0pAC+ieKjFv)n^bEJSv zkh&{p5w~&wOR>s_@_c*c!YMXLH!wOd8jjv9@*_FCbg-o?_GsI-2S=qyb+XjY{pn~k zGq!}DUZoI%Vh#Ca7;`^={v2)Vy!uxp-{0H2eRW;t`k%z7L=M0J`USo&_5&k%XAs_F zOkzFs-~fM;J|tP=w{=qRWjUe|9ZGN9b@5<1H+^ip@yT$pl8z1{3fMVw;(XTbs#Fa zf5&^~)#WGt1EZrTEjRyd9!-e~8sn(@>Usjzjwc)t_Q&2Lb6wqB^vlT#UcqdBPp;o6 zExlx|aZhhgFo`r*B0H5pT{1RAR&hPsBYQFfhFDo^jOZgiwXb_ArFRjV?lo3$iCa?s zLp-;7y5{fR(ejEzd2%`5jb(^Ra;}m4RdGo(a_;vpP!Vv2g4!(O&q0;c|J-mg%*yCs5PTr@uNyy`@A5K^oQhU%y_BOf~*?zSP$qroFHE>5UbBEb#S{-OCPj z$91Vdcz#FIYY@WN`VzJB{EG4BH?y;9DwbAZdm7D&V=QIvtE^@kOM@_IVCDZfG?hX$ zTE0KqJ*^vK?h|81%C7iF#9AC%BJu$5)HPB^MnS>Vh2p~yJq9-aC(r0h&7WcX*2qXp z0I8IL>aQ>Rx&=4bA9{#x4f2FJ9gT?D6>>6^HT;n$LI}8dtiw7ikG_4gJHO5P@};ne z-)3OKimFVj)l9x7-SFPtsc>~j4s3(s1JShkpAKI#r2gd04zb3Q#^&jArweRmF2-u% zKhw#|tL&LYEL($ifRjpLoWA54H?z3-0Bopf$>Np8z~qLzpI=v#d|rICmKMh3dX!Sr zv2Vba1;vBssigy7T#t=~K^91vlX|!OdWP2R%qZHOhk*_wQSb6l2i!1gjrA`hY5r|CoC1NTS3%bAfIf#ytddgf~kqJL)0)qETO2&>@Z=QWWQiLwk0N>7r%v}#?JZnXFm%WQ=?ck;N1jFZ{(8E2an*9w?o`bt z60Sd8)JLbM^<`?YrDfL78%}N*>M}*A_9}RmFOrJ5`$7u$i4(k0e;AK|4ZSe@{d}V0 z9V^>p59*PkP5U`2?oL#i>Jz#1?L%s8D%uYqcsiz7R|3Fs=c~+|E~c0w8Q@>D^>c9t zA19I%^{c#vRWlt=z?FZMVXPE?!wy+-$ikA~jdzl%F#Qx4&Sm8EeOV>^O@^Hjgl;Ql z6|V&_GPu;<>nD{eh#Rnw=8fU{ppf4H9O3TmmzOeIzfYS<#qIS=1#<;+tL*~6TCnkP zgO&*1z(CWR;k*_b@Zw8(`OistbLvGkx+f)HkL#9E+dLH!dpl+IBG@aYtT!SMB2B+%m&?VVrowRn z>6_KnxJOo{-nD^WS66SHigOuJDx9O{=|m|ftp?K0Ddzb(r=5WzC1cBfL=nLmDyCd# zs{?Nul$MQOF`ws@Fv?XeUg)o0ks*N;eCpS3l*YTKWwRq_wrg--S0k>zm1Xe~@ZHCrpJ zW%+!YQRH})X8tR1#O23>SD7KXXOtwZI*H$>DH7(ga{Q~kwSI-)Wd)JXiXW4BT!9(& zVn@D(aN7AQtiCdcqNFVxnaH8cuVF+pACmIBx_K6Es+5+^-DI(>(m|HYJ2hEgC1f*J z`lYHxp3bfOc|0y;1!0+GTE9FS--uc8lx+8vlqp_5wiD?~(e_zR9cR4b=_4NG28B8v z4iR7aIKEugtp_@5CoEw@27t zr(2K}PV=$}-hTum+lVu2kcY5D7rsvhRZD7GgwJ;1({9=l)+Z<;tExE8O*OiKFhvDJ zRxOAXmoPH>RyJo2gPLmRETj|aPdq)6>*&9AqmFz#@iN;}FPN!lrX-r1$+s=-`X?E_ zWP4e#+1aH*@a}g-F3pfadl931OiI$M8u00>MejF>SUBQHK4fzb0HK}IKR-?jTsp;ohFfV#D9V+tPcd@Qbc{+Pyv$ua zp&oaO0nu3Y|K)P~n0~XZ0?fqjt8QY~P{SQ+xGGg%p z)h4dR#ZGkoYEnai=g>NKsyXO>3u<3)?>=WY*+m

<%KE+1XQ9GrzD>!;_htpf~Xi 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 Bu2ld4 literal 0 HcmV?d00001