fix: external styles on svg elements (#2320)

This commit is contained in:
MoyuScript 2020-08-09 14:14:50 +08:00
parent 13ef10a548
commit a89b5ba396
5 changed files with 23 additions and 20 deletions

View File

@ -6,26 +6,19 @@ const fontFamilyParse = (value: string) => fontFamily.parse(Parser.parseValues(v
describe('property-descriptors', () => { describe('property-descriptors', () => {
describe('font-family', () => { describe('font-family', () => {
it('sans-serif', () => it('sans-serif', () => deepEqual(fontFamilyParse('sans-serif'), ['sans-serif']));
deepEqual(fontFamilyParse('sans-serif'), [
"sans-serif",
]));
it('great fonts 40 library', () => it('great fonts 40 library', () =>
deepEqual(fontFamilyParse('great fonts 40 library'), [ deepEqual(fontFamilyParse('great fonts 40 library'), ["'great fonts 40 library'"]));
"'great fonts 40 library'",
]));
it('preferred font, "quoted fallback font", font', () => it('preferred font, "quoted fallback font", font', () =>
deepEqual(fontFamilyParse('preferred font, "quoted fallback font", font'), [ deepEqual(fontFamilyParse('preferred font, "quoted fallback font", font'), [
"'preferred font'", "'preferred font'",
"'quoted fallback font'", "'quoted fallback font'",
"font" 'font'
])); ]));
it("'escaping test\\'s font'", () => it("'escaping test\\'s font'", () =>
deepEqual(fontFamilyParse("'escaping test\\'s font'"), [ deepEqual(fontFamilyParse("'escaping test\\'s font'"), ["'escaping test's font'"]));
"'escaping test\'s font'",
]));
}); });
}); });

View File

@ -32,6 +32,6 @@ export const fontFamily: IPropertyListDescriptor<FontFamily> = {
if (accumulator.length) { if (accumulator.length) {
results.push(accumulator.join(' ')); results.push(accumulator.join(' '));
} }
return results.map(result => result.indexOf(' ') === -1 ? result : `'${result}'`); return results.map(result => (result.indexOf(' ') === -1 ? result : `'${result}'`));
} }
}; };

View File

@ -116,7 +116,7 @@ export class DocumentCloner {
return iframeLoad; return iframeLoad;
} }
createElementClone(node: HTMLElement): HTMLElement { createElementClone<T extends HTMLElement | SVGElement>(node: T): HTMLElement | SVGElement {
if (isCanvasElement(node)) { if (isCanvasElement(node)) {
return this.createCanvasClone(node); return this.createCanvasClone(node);
} }
@ -129,8 +129,7 @@ export class DocumentCloner {
return this.createStyleClone(node); return this.createStyleClone(node);
} }
const clone = node.cloneNode(false) as HTMLElement; const clone = node.cloneNode(false) as T;
// @ts-ignore // @ts-ignore
if (isImageElement(clone) && clone.loading === 'lazy') { if (isImageElement(clone) && clone.loading === 'lazy') {
// @ts-ignore // @ts-ignore
@ -266,14 +265,14 @@ export class DocumentCloner {
const window = node.ownerDocument.defaultView; const window = node.ownerDocument.defaultView;
if (isHTMLElementNode(node) && window) { if (window && isElementNode(node) && (isHTMLElementNode(node) || isSVGElementNode(node))) {
const clone = this.createElementClone(node); const clone = this.createElementClone(node);
const style = window.getComputedStyle(node); const style = window.getComputedStyle(node);
const styleBefore = window.getComputedStyle(node, ':before'); const styleBefore = window.getComputedStyle(node, ':before');
const styleAfter = window.getComputedStyle(node, ':after'); const styleAfter = window.getComputedStyle(node, ':after');
if (this.referenceElement === node) { if (this.referenceElement === node && isHTMLElementNode(clone)) {
this.clonedReferenceElement = clone; this.clonedReferenceElement = clone;
} }
if (isBodyElement(clone)) { if (isBodyElement(clone)) {
@ -307,7 +306,7 @@ export class DocumentCloner {
this.counters.pop(counters); this.counters.pop(counters);
if (style && this.options.copyStyles && !isIFrameElement(node)) { if (style && (this.options.copyStyles || isSVGElementNode(node)) && !isIFrameElement(node)) {
copyCSSStyles(style, clone); copyCSSStyles(style, clone);
} }
@ -487,7 +486,7 @@ const iframeLoader = (iframe: HTMLIFrameElement): Promise<HTMLIFrameElement> =>
}); });
}; };
export const copyCSSStyles = (style: CSSStyleDeclaration, target: HTMLElement): HTMLElement => { export const copyCSSStyles = <T extends HTMLElement | SVGElement>(style: CSSStyleDeclaration, target: T): T => {
// Edge does not provide value for cssText // Edge does not provide value for cssText
for (let i = style.length - 1; i >= 0; i--) { for (let i = style.length - 1; i >= 0; i--) {
const property = style.item(i); const property = style.item(i);

View File

@ -102,7 +102,7 @@ const createsStackingContext = (styles: CSSParsedDeclaration): boolean => styles
export const isTextNode = (node: Node): node is Text => node.nodeType === Node.TEXT_NODE; export const isTextNode = (node: Node): node is Text => node.nodeType === Node.TEXT_NODE;
export const isElementNode = (node: Node): node is Element => node.nodeType === Node.ELEMENT_NODE; export const isElementNode = (node: Node): node is Element => node.nodeType === Node.ELEMENT_NODE;
export const isHTMLElementNode = (node: Node): node is HTMLElement => export const isHTMLElementNode = (node: Node): node is HTMLElement =>
typeof (node as HTMLElement).style !== 'undefined'; isElementNode(node) && typeof (node as HTMLElement).style !== 'undefined' && !isSVGElementNode(node);
export const isSVGElementNode = (element: Element): element is SVGElement => export const isSVGElementNode = (element: Element): element is SVGElement =>
typeof (element as SVGElement).className === 'object'; typeof (element as SVGElement).className === 'object';
export const isLIElement = (node: Element): node is HTMLLIElement => node.tagName === 'LI'; export const isLIElement = (node: Element): node is HTMLLIElement => node.tagName === 'LI';

View File

@ -15,6 +15,12 @@
content: " "; content: " ";
} }
</style> </style>
<style>
.html { font: italic 13px sans-serif; }
.two { font: bold 14px sans-serif; }
.canvas { font: italic 15px serif; fill: red; }
</style>
</head> </head>
<body> <body>
<div> <div>
@ -28,6 +34,11 @@
style="fill:#40aa54;fill-opacity:1;stroke:#20552a;stroke-width:7.99999952;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/> style="fill:#40aa54;fill-opacity:1;stroke:#20552a;stroke-width:7.99999952;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
</g> </g>
</svg> </svg>
<svg width="240" height="160" viewBox="0 0 120 80" xmlns="http://www.w3.org/2000/svg">
<text x="40" y="20" class="html">html</text>
<text x="55" y="25" class="two">2</text>
<text x="65" y="35" class="canvas">canvas</text>
</svg>
<img width="200" height="200" src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjMwNiIgaGVpZ2h0PSIyOTYiPjxkZWZzIGlkPSJkZWZzNCIgLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTYyLjQ2OTk1LC00NzcuMjg2MykiIGlkPSJsYXllcjEiPjxwYXRoIGQ9Im0gMzE0LjE1NzQ1LDQ4MS42OTU1OCBjIC01OS4yMDA4OSwwLjUzNzc0IC0xMTQuODA5NzksMzYuNzIyMTkgLTEzNy4zMTI1LDk1LjM0Mzc1IC0yOS4zOTEyOSw3Ni41NjY5MyA4LjgzOTMyLDE2Mi40NTI0NiA4NS40MDYyNSwxOTEuODQzNzUgbCAzNC4wMzEyNSwtODguNjg3NSBjIC0yMC4wNjc4LC03LjcxMzU4IC0zNC4zMTI1LC0yNy4xNTMyNCAtMzQuMzEyNSwtNDkuOTM3NSAwLC0yOS41NDcyMyAyMy45NTI3NywtNTMuNSA1My41LC01My41IDI5LjU0NzIzLDAgNTMuNSwyMy45NTI3NyA1My41LDUzLjUgMCwyMi43ODQyNiAtMTQuMjQ0Nyw0Mi4yMjM5MiAtMzQuMzEyNSw0OS45Mzc1IGwgMzQuMDMxMjUsODguNjg3NSBjIDM5LjI5MDg1LC0xNS4wODIzNCA3MC4zMjM5LC00Ni4xMTU0IDg1LjQwNjI1LC04NS40MDYyNSAyOS4zOTEyOSwtNzYuNTY2OTMgLTguODM5MzIsLTE2Mi40ODM3MSAtODUuNDA2MjUsLTE5MS44NzUgLTE3Ljk0NTM3LC02Ljg4ODU5IC0zNi40MDg1MywtMTAuMDcwODcgLTU0LjUzMTI1LC05LjkwNjI1IHoiIGlkPSJwYXRoMjgzMCIgc3R5bGU9ImZpbGw6IzQwYWE1NDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzIwNTUyYTtzdHJva2Utd2lkdGg6Ny45OTk5OTk1MjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPjwvZz48L3N2Zz4=" /></div> <img width="200" height="200" src="data:image/svg+xml;base64,PHN2ZyB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjMwNiIgaGVpZ2h0PSIyOTYiPjxkZWZzIGlkPSJkZWZzNCIgLz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTYyLjQ2OTk1LC00NzcuMjg2MykiIGlkPSJsYXllcjEiPjxwYXRoIGQ9Im0gMzE0LjE1NzQ1LDQ4MS42OTU1OCBjIC01OS4yMDA4OSwwLjUzNzc0IC0xMTQuODA5NzksMzYuNzIyMTkgLTEzNy4zMTI1LDk1LjM0Mzc1IC0yOS4zOTEyOSw3Ni41NjY5MyA4LjgzOTMyLDE2Mi40NTI0NiA4NS40MDYyNSwxOTEuODQzNzUgbCAzNC4wMzEyNSwtODguNjg3NSBjIC0yMC4wNjc4LC03LjcxMzU4IC0zNC4zMTI1LC0yNy4xNTMyNCAtMzQuMzEyNSwtNDkuOTM3NSAwLC0yOS41NDcyMyAyMy45NTI3NywtNTMuNSA1My41LC01My41IDI5LjU0NzIzLDAgNTMuNSwyMy45NTI3NyA1My41LDUzLjUgMCwyMi43ODQyNiAtMTQuMjQ0Nyw0Mi4yMjM5MiAtMzQuMzEyNSw0OS45Mzc1IGwgMzQuMDMxMjUsODguNjg3NSBjIDM5LjI5MDg1LC0xNS4wODIzNCA3MC4zMjM5LC00Ni4xMTU0IDg1LjQwNjI1LC04NS40MDYyNSAyOS4zOTEyOSwtNzYuNTY2OTMgLTguODM5MzIsLTE2Mi40ODM3MSAtODUuNDA2MjUsLTE5MS44NzUgLTE3Ljk0NTM3LC02Ljg4ODU5IC0zNi40MDg1MywtMTAuMDcwODcgLTU0LjUzMTI1LC05LjkwNjI1IHoiIGlkPSJwYXRoMjgzMCIgc3R5bGU9ImZpbGw6IzQwYWE1NDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6IzIwNTUyYTtzdHJva2Utd2lkdGg6Ny45OTk5OTk1MjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1vcGFjaXR5OjE7c3Ryb2tlLWRhc2hhcnJheTpub25lIiAvPjwvZz48L3N2Zz4=" /></div>
</body> </body>
</html> </html>