<commit_before>import * as XmlSerializer from './XmlSerializer';
import { ColorUtil } from 'app/scripts/common';
import {
  GroupLayer,
  Layer,
  PathLayer,
  VectorLayer,
} from 'app/scripts/model/layers';
import * as _ from 'lodash';

const XMLNS_NS = 'http://www.w3.org/2000/xmlns/';
const SVG_NS = 'http://www.w3.org/2000/svg';

/**
 * Serializes an VectorLayer to a vector drawable XML file.
 */
export function toSvgString(
  vectorLayer: VectorLayer,
  width?: number,
  height?: number,
  x?: number,
  y?: number,
  withIdsAndNS = true,
) {
  const xmlDoc = document.implementation.createDocument(undefined, 'svg', undefined);
  const rootNode = xmlDoc.documentElement;
  vectorLayerToSvgNode(vectorLayer, rootNode, xmlDoc, withIdsAndNS);
  if (width !== undefined) {
    rootNode.setAttributeNS(undefined, 'width', width.toString() + 'px');
  }
  if (height !== undefined) {
    rootNode.setAttributeNS(undefined, 'height', height.toString() + 'px');
  }
  if (x !== undefined) {
    rootNode.setAttributeNS(undefined, 'x', x.toString() + 'px');
  }
  if (y !== undefined) {
    rootNode.setAttributeNS(undefined, 'y', y.toString() + 'px');
  }
  return serializeXmlNode(rootNode);
}

/**
 * Helper method that serializes a VectorLayer to a destinationNode in an xmlDoc.
 * The destinationNode should be a <vector> node.
 */
function vectorLayerToSvgNode(
  vl: VectorLayer,
  destinationNode: HTMLElement,
  xmlDoc: Document,
  withIdsAndNS = true,
) {
  if (withIdsAndNS) {
    destinationNode.setAttributeNS(XMLNS_NS, 'xmlns', SVG_NS);
  }
  destinationNode.setAttributeNS(undefined, 'viewBox', `0 0 ${vl.width} ${vl.height}`);

  walk(vl, (layer, parentNode) => {
    if (layer instanceof VectorLayer) {
      if (withIdsAndNS) {
        conditionalAttr(destinationNode, 'id', vl.name, '');
      }
      conditionalAttr(destinationNode, 'opacity', vl.alpha, 1);
      return parentNode;
    }
    if (layer instanceof PathLayer) {
      const node = xmlDoc.createElement('path');
      if (withIdsAndNS) {
        conditionalAttr(node, 'id', layer.name);
      }
      const path = layer.pathData;
      conditionalAttr(node, 'd', path ? path.getPathString() : '');
      if (layer.fillColor) {
        conditionalAttr(node, 'fill', ColorUtil.androidToCssHexColor(layer.fillColor), '');
      } else {
        conditionalAttr(node, 'fill', 'none');
      }
      conditionalAttr(node, 'fill-opacity', layer.fillAlpha, 1);
      if (layer.strokeColor) {
        conditionalAttr(node, 'stroke', ColorUtil.androidToCssHexColor(layer.strokeColor), '');
      }
      conditionalAttr(node, 'stroke-opacity', layer.strokeAlpha, 1);
      conditionalAttr(node, 'stroke-width', layer.strokeWidth, 0);
      // TODO: support exporting trim paths to SVG
      // conditionalAttr(node, 'android:trimPathStart', layer.trimPathStart, 0);
      // conditionalAttr(node, 'android:trimPathEnd', layer.trimPathEnd, 1);
      // conditionalAttr(node, 'android:trimPathOffset', layer.trimPathOffset, 0);
      conditionalAttr(node, 'stroke-linecap', layer.strokeLinecap, 'butt');
      conditionalAttr(node, 'stroke-linejoin', layer.strokeLinejoin, 'miter');
      conditionalAttr(node, 'stroke-miterlimit', layer.strokeMiterLimit, 4);
      const fillRule =
        !layer.fillType || layer.fillType === 'nonZero' ? 'nonzero' : 'evenodd';
      conditionalAttr(node, 'fill-rule', fillRule, 'nonzero');
      parentNode.appendChild(node);
      return parentNode;
    }
    if (layer instanceof GroupLayer) {
      // TODO: create one node per group property being animated
      const node = xmlDoc.createElement('g');
      if (withIdsAndNS) {
        conditionalAttr(node, 'id', layer.name);
      }
      const transformValues: string[] = [];
      if (layer.scaleX !== 1 || layer.scaleY !== 1) {
        transformValues.push(`scale(${layer.scaleX} ${layer.scaleY}`);
      }
      if (layer.rotation) {
        transformValues.push(`rotate(${layer.rotation} ${layer.pivotX} ${layer.pivotY})`);
      }
      if (layer.translateX || layer.translateY) {
        transformValues.push(`translate(${layer.translateX} ${layer.translateY})`);
      }
      if (transformValues.length) {
        node.setAttributeNS(undefined, 'transform', transformValues.join(' '));
      }
      parentNode.appendChild(node);
      return node;
    }
    // TODO: support exporting clip paths to SVG
    /* else if (layer instanceof ClipPathLayer) {
    const node = xmlDoc.createElement('clip-path');
    conditionalAttr(node, '', layer.name);<commit_msg>fix typo in svg serializer<commit_after>import * as XmlSerializer from './XmlSerializer';
import { ColorUtil } from 'app/scripts/common';
import {
  GroupLayer,
  Layer,
  PathLayer,
  VectorLayer,
} from 'app/scripts/model/layers';
import * as _ from 'lodash';

const XMLNS_NS = 'http://www.w3.org/2000/xmlns/';
const SVG_NS = 'http://www.w3.org/2000/svg';

/**
 * Serializes an VectorLayer to a vector drawable XML file.
 */
export function toSvgString(
  vectorLayer: VectorLayer,
  width?: number,
  height?: number,
  x?: number,
  y?: number,
  withIdsAndNS = true,
) {
  const xmlDoc = document.implementation.createDocument(undefined, 'svg', undefined);
  const rootNode = xmlDoc.documentElement;
  vectorLayerToSvgNode(vectorLayer, rootNode, xmlDoc, withIdsAndNS);
  if (width !== undefined) {
    rootNode.setAttributeNS(undefined, 'width', width.toString() + 'px');
  }
  if (height !== undefined) {
    rootNode.setAttributeNS(undefined, 'height', height.toString() + 'px');
  }
  if (x !== undefined) {
    rootNode.setAttributeNS(undefined, 'x', x.toString() + 'px');
  }
  if (y !== undefined) {
    rootNode.setAttributeNS(undefined, 'y', y.toString() + 'px');
  }
  return serializeXmlNode(rootNode);
}

/**
 * Helper method that serializes a VectorLayer to a destinationNode in an xmlDoc.
 * The destinationNode should be a <vector> node.
 */
function vectorLayerToSvgNode(
  vl: VectorLayer,
  destinationNode: HTMLElement,
  xmlDoc: Document,
  withIdsAndNS = true,
) {
  if (withIdsAndNS) {
    destinationNode.setAttributeNS(XMLNS_NS, 'xmlns', SVG_NS);
  }
  destinationNode.setAttributeNS(undefined, 'viewBox', `0 0 ${vl.width} ${vl.height}`);

  walk(vl, (layer, parentNode) => {
    if (layer instanceof VectorLayer) {
      if (withIdsAndNS) {
        conditionalAttr(destinationNode, 'id', vl.name, '');
      }
      conditionalAttr(destinationNode, 'opacity', vl.alpha, 1);
      return parentNode;
    }
    if (layer instanceof PathLayer) {
      const node = xmlDoc.createElement('path');
      if (withIdsAndNS) {
        conditionalAttr(node, 'id', layer.name);
      }
      const path = layer.pathData;
      conditionalAttr(node, 'd', path ? path.getPathString() : '');
      if (layer.fillColor) {
        conditionalAttr(node, 'fill', ColorUtil.androidToCssHexColor(layer.fillColor), '');
      } else {
        conditionalAttr(node, 'fill', 'none');
      }
      conditionalAttr(node, 'fill-opacity', layer.fillAlpha, 1);
      if (layer.strokeColor) {
        conditionalAttr(node, 'stroke', ColorUtil.androidToCssHexColor(layer.strokeColor), '');
      }
      conditionalAttr(node, 'stroke-opacity', layer.strokeAlpha, 1);
      conditionalAttr(node, 'stroke-width', layer.strokeWidth, 0);
      // TODO: support exporting trim paths to SVG
      // conditionalAttr(node, 'android:trimPathStart', layer.trimPathStart, 0);
      // conditionalAttr(node, 'android:trimPathEnd', layer.trimPathEnd, 1);
      // conditionalAttr(node, 'android:trimPathOffset', layer.trimPathOffset, 0);
      conditionalAttr(node, 'stroke-linecap', layer.strokeLinecap, 'butt');
      conditionalAttr(node, 'stroke-linejoin', layer.strokeLinejoin, 'miter');
      conditionalAttr(node, 'stroke-miterlimit', layer.strokeMiterLimit, 4);
      const fillRule =
        !layer.fillType || layer.fillType === 'nonZero' ? 'nonzero' : 'evenodd';
      conditionalAttr(node, 'fill-rule', fillRule, 'nonzero');
      parentNode.appendChild(node);
      return parentNode;
    }
    if (layer instanceof GroupLayer) {
      // TODO: create one node per group property being animated
      const node = xmlDoc.createElement('g');
      if (withIdsAndNS) {
        conditionalAttr(node, 'id', layer.name);
      }
      const transformValues: string[] = [];
      if (layer.scaleX !== 1 || layer.scaleY !== 1) {
        transformValues.push(`scale(${layer.scaleX} ${layer.scaleY})`);
      }
      if (layer.rotation) {
        transformValues.push(`rotate(${layer.rotation} ${layer.pivotX} ${layer.pivotY})`);
      }
      if (layer.translateX || layer.translateY) {
        transformValues.push(`translate(${layer.translateX} ${layer.translateY})`);
      }
      if (transformValues.length) {
        node.setAttributeNS(undefined, 'transform', transformValues.join(' '));
      }
      parentNode.appendChild(node);
      return node;
    }
    // TODO: support exporting clip paths to SVG
    /* else if (layer instanceof ClipPathLayer) {
    const node = xmlDoc.createElement('clip-path');
    conditionalAttr(node, '', layer.name);
{'fraction_non_alphanumeric': 0.14560862865947613, 'fraction_numerical': 0.004842615012106538, 'mean_word_length': 3.5686274509803924, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 20}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5615113', 'n_tokens_mistral': 2836, 'n_tokens_neox': 2781, 'n_words': 615}