diff --git a/packages/core/src/textures/resources/SVGResource.ts b/packages/core/src/textures/resources/SVGResource.ts index c7d1183339..e488200069 100644 --- a/packages/core/src/textures/resources/SVGResource.ts +++ b/packages/core/src/textures/resources/SVGResource.ts @@ -116,6 +116,8 @@ export class SVGResource extends BaseImageResource resolve(this); }; + const svgString = this.svg; + // Convert SVG inline string to data-uri if (SVGResource.SVG_XML.test(this.svg.trim())) { @@ -126,7 +128,7 @@ export class SVGResource extends BaseImageResource (this as any).svg = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(this.svg)))}`; } - this._loadSvg(); + this._loadSvg(svgString); }); return this._load; @@ -134,10 +136,11 @@ export class SVGResource extends BaseImageResource /** * Loads an SVG image from `imageUrl` or `data URL`. - * + * @param {string} svgString - the raw string of SVG for falling back to + * viewBox when width or height is not specified. This happens in Firefox. * @private */ - private _loadSvg(): void + private _loadSvg(svgString: string): void { const tempImage = new Image(); @@ -162,12 +165,21 @@ export class SVGResource extends BaseImageResource return; } - const svgWidth = tempImage.width; - const svgHeight = tempImage.height; + let svgWidth = tempImage.width; + let svgHeight = tempImage.height; if (!svgWidth || !svgHeight) { - throw new Error('The SVG image must have width and height defined (in pixels), canvas API needs them.'); + const size = SVGResource.parseWidthHeightFromViewBox(svgString); + + svgWidth = size.width; + svgHeight = size.height; + } + + if (!svgWidth || !svgHeight) + { + throw new Error('The SVG image must have width, height' + + 'or viewBox defined (in pixels), canvas API needs them.'); } // Set render size @@ -199,6 +211,46 @@ export class SVGResource extends BaseImageResource }; } + /** + * This function gets the width and height from the viewBox attribute from + * provided SVG. + * @param {string} svg - the string of the SVG, not base64 encoded. + * @param {ISize} scaleTo - optional, if provide, will try to scale viewBox + * the provided size. + * @return {PIXI.ISize} scaled viewBox size if parse success, return (0, 0) + * otherwise. + */ + static parseWidthHeightFromViewBox(svg: string, scaleTo?: ISize): ISize + { + const size: ISize = { width: 0, height: 0 }; + + try + { + const parser = new DOMParser(); + const svgDocument = parser.parseFromString(svg, 'image/svg+xml'); + const element = svgDocument.querySelector('svg'); + + size.width = element.viewBox.baseVal.width; + size.height = element.viewBox.baseVal.height; + if (scaleTo === null || scaleTo === undefined) return size; + + const factor = scaleTo.height < scaleTo.width + ? scaleTo.height / size.height : scaleTo.width / size.width; + + size.width *= factor; + size.height *= factor; + + return size; + } + catch (err) + { + size.width = 0; + size.height = 0; + + return size; + } + } + /** * Get size from an svg string using regexp. * diff --git a/packages/core/test/SVGResource.tests.ts b/packages/core/test/SVGResource.tests.ts index e64deda431..ce326e6470 100644 --- a/packages/core/test/SVGResource.tests.ts +++ b/packages/core/test/SVGResource.tests.ts @@ -209,6 +209,36 @@ describe('SVGResource', function () expect(Object.keys(svgSize).length) .to.equal(0); }); + + it('should get size from viewBox', function () + { + const url = path.join(this.resources, 'viewBoxOnly.svg'); + const svgString = fs.readFileSync(url, 'utf8'); + const svgSize = SVGResource.parseWidthHeightFromViewBox(svgString); + + expect(svgString.includes('viewBox')).to.equal(true); + expect(svgSize.width).to.equal(8); + expect(svgSize.height).to.equal(11); + }); + + it('should get scaled size from viewBox', function () + { + const url = path.join(this.resources, 'viewBoxOnly.svg'); + const svgString = fs.readFileSync(url, 'utf8'); + let scale = { width: 16, height: 100 }; + let svgSize = SVGResource.parseWidthHeightFromViewBox(svgString, scale); + + expect(svgString.includes('viewBox')).to.equal(true); + expect(svgSize.width).to.equal(16); + expect(svgSize.height).to.equal(22); + + scale = { width: 100, height: 33 }; + svgSize = SVGResource.parseWidthHeightFromViewBox(svgString, scale); + + expect(svgString.includes('viewBox')).to.equal(true); + expect(svgSize.width).to.equal(24); + expect(svgSize.height).to.equal(33); + }); }); describe('test', function () diff --git a/packages/core/test/resources/viewBoxOnly.svg b/packages/core/test/resources/viewBoxOnly.svg new file mode 100644 index 0000000000..88a3e02a7e --- /dev/null +++ b/packages/core/test/resources/viewBoxOnly.svg @@ -0,0 +1,13 @@ + + + + + 9 + + \ No newline at end of file