function SVGShape(file, spriter) {
  this.source = file;
  this.spriter = spriter;
  this.svg = { current: this.source.contents.toString(), ready: null };
  this.name = this.source.path.substr(this.source.base.length + path.sep.length);
  // todo: get rid of setting empty object if this.spriter.config.shape is not provided
  // https://github.com/svg-sprite/svg-sprite/pull/653#discussion_r841069781
  this.config = merge(merge({}, defaultConfig), this.spriter.config.shape || {});

  if (!isFunction(this.config.id.generator)) {
    this.config.id.generator = createIdGenerator(isString(this.config.id.generator) ? this.config.id.generator + (this.config.id.generator.includes('%s') ? '' : '%s') : '%s');
  }

  this.id = this.config.id.generator(this.name, this.source);
  this.state = this.id.split(this.config.id.pseudo);
  this.base = this.state.shift();
  this.state = this.state.shift() || null;
  this.master = null;
  this.copies = 0;
  this._precision = 10 ** Number(this.config.dimension.precision);
  this._scale = 1;
  this._namespaced = false;

  // Determine meta & alignment data
  const relative = path.basename(this.source.relative, '.svg');
  this.meta = this.id in this.config.meta ? this.config.meta[this.id] : (relative in this.config.meta ? this.config.meta[relative] : {});
  this.align = Object.entries({ ...this.config.align['*'], ...(this.id in this.config.align ? this.config.align[this.id] : (relative in this.config.align ? this.config.align[relative] : {})) });

  // Initially set the SVG of this shape
  this._initSVG();

  // XML declaration and doctype
  const xmldecl = this.svg.current.match(/<\?xml.*?>/g);
  const doctype = this.svg.current.match(/<!DOCTYPE.*?>/g);
  this.xmlDeclaration = xmldecl ? xmldecl[0] : DEFAULT_XML_DECLARATION;
  this.doctypeDeclaration = doctype ? doctype[0] : '';

  this.spriter.verbose('Added shape "%s:%s"', this.base, this.state || 'regular');
}
SVGShape

@param {File} file                   Vinyl file
@param {SVGSpriter} spriter          Spriter instance