Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix(SVGParser) ignore missing xlink target issue on svg parsing #9427

Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## [next]

- fix(parser): fix svg's with use tags containing bad href's not loading [#9109](https://github.com/fabricjs/fabric.js/issues/9109)

## [6.0.0-beta13]

- fix(Object): fixes centeredScaling prop type [#9401](https://github.com/fabricjs/fabric.js/pull/9401)
Expand Down
18 changes: 0 additions & 18 deletions src/parser/elementById.ts

This file was deleted.

34 changes: 34 additions & 0 deletions src/parser/loadSVGFromString.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Path } from '../shapes/Path';
import { loadSVGFromString } from './loadSVGFromString';

describe('loadSVGFromString', () => {
it('returns successful parse of svg with use tag containing bad reference', async () => {
// in this case, ignore bad use but still load rest of svg
const str = `<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="10" height="10" />
<use href="#missing" x="50" y="50" ></use>
</svg>`;

const parsedSvg = await loadSVGFromString(str);
expect(parsedSvg.objects[0]).not.toBeNull();
if (parsedSvg.objects[0] !== null) {
expect(parsedSvg.objects[0].isType('Rect')).toBe(true);
}
});

it('returns successful parse of svg with use tag containing bad clip-path', async () => {
// in this case, load svg but ignore clip-path attribute in <use>
const str = `<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path id="heart" d="M10,30 A20,20,0,0,1,50,30 A20,20,0,0,1,90,30 Q90,60,50,90 Q10,60,10,30 Z" />
<use href="#heart" fill="red" />
</svg>`;
// need to load Path here for it to populate in class registry; loadSvgFromString does not
// import Path so we'd fail the test without this.
const unused = Path.name;

const parsedSvg = await loadSVGFromString(str);
if (parsedSvg.objects[0] !== null) {
expect(parsedSvg.objects[0].isType('Path')).toBe(true);
}
});
});
8 changes: 6 additions & 2 deletions src/parser/parseUseDirectives.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { svgNS } from './constants';
import { elementById } from './elementById';
import { getMultipleNodes } from './getMultipleNodes';
import { applyViewboxTransform } from './applyViewboxTransform';

Expand All @@ -17,7 +16,12 @@ export function parseUseDirectives(doc: Document) {
const xlink = xlinkAttribute.slice(1);
const x = el.getAttribute('x') || 0;
const y = el.getAttribute('y') || 0;
let el2 = elementById(doc, xlink)!.cloneNode(true) as Element;
const el2Orig = doc.getElementById(xlink);
if (el2Orig === null) {
// if we can't find the target of the xlink, consider this use tag bad, similar to no xlink
return;
}
let el2 = el2Orig.cloneNode(true) as Element;
let currentTrans =
(el2.getAttribute('transform') || '') +
' translate(' +
Expand Down
4 changes: 1 addition & 3 deletions src/parser/recursivelyParseGradientsXlink.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { elementById } from './elementById';

const gradientsAttrs = [
'gradientTransform',
'x1',
Expand All @@ -20,7 +18,7 @@ export function recursivelyParseGradientsXlink(
gradient: Element
) {
const xLink = gradient.getAttribute(xlinkAttr)?.slice(1) || '',
referencedGradient = elementById(doc, xLink);
referencedGradient = doc.getElementById(xLink);
if (referencedGradient && referencedGradient.getAttribute(xlinkAttr)) {
recursivelyParseGradientsXlink(doc, referencedGradient as Element);
}
Expand Down