<commit_before>/**
 * svgPrune
 * Simple JavaScript library to simplify SVG files.
 *
 * Copyright (c) 2013 <NAME> <<EMAIL>>
 *
 * Licensed under MIT license. See LICENSE.
 */

'use strict';

var _       = require('lodash');
var XMLDOMParser = require('xmldom').DOMParser;
var SvgPath = require('svgpath');

/*
  Recursive exploring XML DOM tree to determine all present tags (names)
  context should be an array to collect results
**/
function nodeExplorer(targetNode) {
  for (var node = targetNode.firstChild; node; node = node.nextSibling) {
    if (! node.data) {
      this.push(node.nodeName.toLowerCase());
    }

    if (node.firstChild) {
      nodeExplorer.call(this, node);
    }
  }
}

/*
  Determining height & width from path
**/
function calculatePathBounds(path) { 
  var bounds = {
    height: 0,
    width: 0
  };

  new SvgPath(path).iterate(function(segment) {
    var command = segment[0];
    if (! _.contains(['V', 'A'], command)) {
      segment.forEach(function(val, i) {
        if (!i) { return; } // skip command
        if (i % 2) {
          bounds.width = bounds.width<val ? val : bounds.width;
        }
        else {
          bounds.height = bounds.height<val ? val : bounds.height;
        }
      });
    }
  });

  return bounds;
}

/*
  data - XML SVG text content

  result structure:
  ---
  error: Number
  path: String
  ok: Boolean
  skippedTags: Array
  skippedAttributes: Array
  x: Number
  y: Number
  height: Number
  width: Number
**/
var svgPrune = function(data) {
  var xmlDoc;

  /* the result */
  var result = {
    error: 0,
    path: '',
    ok: false,
    skippedTags: [],
    skippedAttributes: [],
    x: 0,
    y: 0,
    height: 0,
    width: 0
  };

  try {
    xmlDoc = new XMLDOMParser({ errorHandler:{} }).parseFromString(data, 'application/xml');
  }
  catch(e) {
    result.error = 1;
  }

  // TODO: getElementsByTagName are case-sensitive. 
  var svgTag = xmlDoc.getElementsByTagName('svg')[0];

  /* Check svg */
  if (! svgTag) {
    result.error = 1;
  }

  if (result.error) {
    return result;
  }

  /* Recursive collect all tags */
  var tags = [];
  nodeExplorer.call(tags, svgTag);

  /*
    TODO: _.without list for skipped tags should be extended 
    when we add a support for conversion other tags to path
  **/
  result.skippedTags = _.without(tags, 'g', 'path');

  var pathTags = xmlDoc.getElementsByTagName('path');

  /* Going over all paths and applying all group transformations */
  for (var i=0; i<pathTags.length; i++) {
    var node = pathTags.item(i);
    var path = node.getAttribute('d');
    var pathAttrsSkip = _.without(_.map( node.attributes, function(attr) { return attr.nodeName.toLowerCase(); }), 'transform', 'd');
    result.skippedAttributes = _.union(result.skippedAttributes, pathAttrsSkip);

    /* Applying path transform if any */
    var pathTransform = node.getAttribute('transform');
    if (pathTransform) {
      path = new SvgPath(path).transform(pathTransform).toString();
    }

    /*
      TODO: tags->path conversion should be implemented here..
    **/

    for (var group = node.parentNode; group.nodeName == 'g'; group = group.parentNode) {
      var groupAttrsSkip = _.without(_.map(group.attributes, function(attr) { return attr.nodeName.toLowerCase(); }), 'transform');
      result.skippedAttributes = _.union(result.skippedAttributes, groupAttrsSkip);

      /* Applyin group transformation if any */
      var groupTransform = group.getAttribute('transform');
      if (groupTransform) {
        path = new SvgPath(path).transform(groupTransform).toString();
      }
    }

    result.path = result.path.concat(path); 
  }

  /* Calculating x,y,height,width */
  var viewBox;
  var svgAttrs = {};
  var viewBoxAttr = svgTag.getAttribute('viewBox');
  if (viewBoxAttr) {
    viewBox = _.map(viewBoxAttr.split(' '), 
      function(val) { return parseInt(val, 10); }
    );
  } 

  _.forEach(['x', 'y', 'width', 'height'], function(key) {
    svgAttrs[key] = parseInt(svgTag.getAttribute(key), 10);
  });

  if (viewBox) {
    result.x = viewBox[0];
    result.y = viewBox[1];
    result.height = viewBox[2];
    result.width = viewBox[3];
  }
  else if (svgAttrs.height && svgAttrs.width) {
    result.height = svgAttrs.height;
    result.width = svgAttrs.width;
  }
  else {
    var bounds = calculatePathBounds(result.path);
    result.height = bounds.height;
    result.width = bounds.width;
  }

  result.ok = ! (result.skippedTags.length || result.skippedAttributes.length); 
  return result;
};

module.exports.svgPrune = svgPrune;
<commit_msg>Another algo collecting allowed/disallowed tags/attrs<commit_after>/**
 * svgPrune
 * Simple JavaScript library to simplify SVG files.
 *
 * Copyright (c) 2013 <NAME> <<EMAIL>>
 *
 * Licensed under MIT license. See LICENSE.
 */

'use strict';

var _       = require('lodash');
var XMLDOMParser = require('xmldom').DOMParser;
var SvgPath = require('svgpath');

var allowedSvgTags = ['svg', 'g', 'path', 'title'];
var disallowedSvgAttributes = ['fill', 'stroke', 'stroke-width'];

/*
  Recursive exploring XML DOM tree to determine all present tags (names)
  context should be an array to collect results
**/
function nodeExplorer(targetNode) {
  for (var node = targetNode.firstChild; node; node = node.nextSibling) {
    if (! node.data) {
      this.push(node.nodeName.toLowerCase());
    }

    if (node.firstChild) {
      nodeExplorer.call(this, node);
    }
  }
}

/*
  Determining height & width from path
**/
function calculatePathBounds(path) { 
  var bounds = {
    height: 0,
    width: 0
  };

  new SvgPath(path).iterate(function(segment) {
    var command = segment[0];
    if (! _.contains(['V', 'A'], command)) {
      segment.forEach(function(val, i) {
        if (!i) { return; } // skip command
        if (i % 2) {
          bounds.width = bounds.width<val ? val : bounds.width;
        }
        else {
          bounds.height = bounds.height<val ? val : bounds.height;
        }
      });
    }
  });

  return bounds;
}

/*
  data - XML SVG text content

  result structure:
  ---
  error: Number
  path: String
  ok: Boolean
  skippedTags: Array
  skippedAttributes: Array
  x: Number
  y: Number
  height: Number
  width: Number
**/
var svgPrune = function(data) {
  var xmlDoc;

  /* the result */
  var result = {
    error: 0,
    path: '',
    ok: false,
    skippedTags: [],
    skippedAttributes: [],
    x: 0,
    y: 0,
    height: 0,
    width: 0
  };

  try {
    xmlDoc = new XMLDOMParser({ errorHandler:{} }).parseFromString(data, 'application/xml');
  }
  catch(e) {
    result.error = 1;
  }

  // TODO: getElementsByTagName are case-sensitive. 
  var svgTag = xmlDoc.getElementsByTagName('svg')[0];

  /* Check svg */
  if (! svgTag) {
    result.error = 1;
  }

  if (result.error) {
    return result;
  }

  /* Recursive collect all tags */
  var tags = [];
  nodeExplorer.call(tags, svgTag);
  result.skippedTags = _.difference(tags, allowedSvgTags);

  var pathTags = xmlDoc.getElementsByTagName('path');

  /* Going over all paths and applying all group transformations */
  for (var i=0; i<pathTags.length; i++) {
    var node = pathTags.item(i);
    var path = node.getAttribute('d');

    /* Looking for disallowed attributes at path */
    var pathAttrsSkip = _.intersection(disallowedSvgAttributes, _.map( node.attributes, function(attr) { return attr.nodeName.toLowerCase(); }));
    result.skippedAttributes = _.union(result.skippedAttributes, pathAttrsSkip);


    /* Applying path transform if any */
    var pathTransform = node.getAttribute('transform');
    if (pathTransform) {
      path = new SvgPath(path).transform(pathTransform).toString();
    }

    /*
      TODO: tags->path conversion should be implemented here..
    **/

    for (var group = node.parentNode; group.nodeName == 'g'; group = group.parentNode) {
      /* Looking for disallowed attributes at group tag */
      var groupAttrsSkip = _.intersection(disallowedSvgAttributes, _.map(group.attributes, function(attr) { return attr.nodeName.toLowerCase(); }));
      result.skippedAttributes = _.union(result.skippedAttributes, groupAttrsSkip);

      /* Applyin group transformation if any */
      var groupTransform = group.getAttribute('transform');
      if (groupTransform) {
        path = new SvgPath(path).transform(groupTransform).toString();
      }
    }

    result.path = result.path.concat(path); 
  }

  /* Calculating x,y,height,width */
  var viewBox;
  var svgAttrs = {};
  var viewBoxAttr = svgTag.getAttribute('viewBox');
  if (viewBoxAttr) {
    viewBox = _.map(viewBoxAttr.split(' '), 
      function(val) { return parseInt(val, 10); }
    );
  } 

  _.forEach(['x', 'y', 'width', 'height'], function(key) {
    svgAttrs[key] = parseInt(svgTag.getAttribute(key), 10);
  });

  if (viewBox) {
    result.x = viewBox[0];
    result.y = viewBox[1];
    result.height = viewBox[2];
    result.width = viewBox[3];
  }
  else if (svgAttrs.height && svgAttrs.width) {
    result.height = svgAttrs.height;
    result.width = svgAttrs.width;
  }
  else {
    var bounds = calculatePathBounds(result.path);
    result.height = bounds.height;
    result.width = bounds.width;
  }

  result.ok = ! (result.skippedTags.length || result.skippedAttributes.length); 
  return result;
};

module.exports.svgPrune = svgPrune;

{'fraction_non_alphanumeric': 0.14144983402933933, 'fraction_numerical': 0.005353892279687333, 'mean_word_length': 2.9710884353741496, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 12}, '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': '6387860', 'n_tokens_mistral': 3080, 'n_tokens_neox': 2989, 'n_words': 730}