Home Reference Source

src/asset-loaders/SVGAsset.js

import Asset from '../systems/AssetSystem/Asset';

const viewBoxPattern =
  /viewBox=\"(\d+(\.\d+)?)\s+(\d+(\.\d+)?)\s+(\d+(\.\d+)?)\s+(\d+(\.\d+)?)\"/;

/**
 * SVG image asset loader.
 */
export default class SVGAsset extends Asset {

  /**
   * Asset factory.
   *
   * @param {*}	args - Factory parameters.
   *
   * @return {SVGAsset} Asset instance.
   *
   * @example
   * system.registerProtocol('svg', SVGAsset.factory);
   */
  static factory(...args) {
    return new SVGAsset(...args);
  }

  /**
   * @override
   */
  dispose() {
    if (!!this.data) {
      delete this.data.src;
    }

    super.dispose();
  }

  /**
   * @override
   */
  load() {
    const { filename, owner } = this;

    return owner.fetchEngine(owner.pathPrefix + filename, owner.fetchOptions)
      .then(response => !!response.ok
        ? response.text()
        : Promise.reject(new Error(`Cannot load SVG image file: ${filename}`))
      )
      .then(data => new Promise((resolve, reject) => {
        const viewBox = viewBoxPattern.exec(data);
        if (!viewBox) {
          reject(new Error(`Cannot parse SVG image viewBox property: ${filename}`));
          return;
        }
        const b64 = `data:image/svg+xml;base64,${btoa(data)}`;
        const image = new Image();
        image.width = parseFloat(viewBox[5]) - parseFloat(viewBox[1]);
        image.height = parseFloat(viewBox[7]) - parseFloat(viewBox[3]);

        image.onload = () => {
          const { options } = this;
          if (!!options) {
            if ('scale' in options && typeof options.scale === 'number') {
              image.width *= options.scale;
              image.height *= options.scale;
            }
          }
          this.data = image;

          resolve(this);
        };
        image.onerror = error => reject(`Cannot read SVG image file: ${filename}`);

        image.src = b64;
      }));
  }

}