Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove empty g elements #42

Merged
merged 6 commits into from
Jul 24, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,52 @@ module.exports = function(grunt) {
files: {
'tmp/cleanup.svg': ['test/fixtures/cleanup.svg']
}
},

removeunreferencedids: {
options: {
cleanup: true,
cleanupdefs: true
},
files: {
'tmp/no_unref_ids.svg': ['test/fixtures/usingdef.svg']
}
},

cleanupfill: {
options: {
cleanup: ['fill']
},
files: {
'tmp/cleanup_fill.svg': ['test/fixtures/dribbble.svg']
}
},

nocleandefs: {
options: {
cleanup: ['style']
},
files: {
'tmp/defs_noclean.svg': ['test/fixtures/usingdef.svg']
}
},

cleandefs: {
options: {
cleanup: ['style'],
cleanupdefs: true
},
files: {
'tmp/defs_clean.svg': ['test/fixtures/usingdef.svg']
}
},

removeemptyg: {
files: {
'tmp/no_empty_g.svg': ['test/fixtures/scissors.svg']
}
}

},

// Unit tests.
Expand Down
123 changes: 77 additions & 46 deletions tasks/svgstore.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,6 @@ module.exports = function (grunt) {
return crypto.createHash('md5').update(str).digest('hex');
};

var symbolAttributes = function(attrs){
if (typeof attrs === 'undefined') {return '';}

var result = '';
Object.keys(attrs).forEach(function (key) {
result += ' ' + key + '="' + attrs[key] + '"';
});
return result;
};

var convertNameToId = function( name ){
var dotPos = name.indexOf('.');
if ( dotPos > -1){
Expand All @@ -38,7 +28,7 @@ module.exports = function (grunt) {
return name;
};

// Matching an url() reference. To correct references broken by making ids unquie to the source svg
// Matching an url() reference. To correct references broken by making ids unique to the source svg
var urlPattern = /url\(\s*#([^ ]+?)\s*\)/g;

// Please see the Grunt documentation for more information regarding task
Expand All @@ -53,10 +43,17 @@ module.exports = function (grunt) {
},
formatting: false,
includedemo: false,
symbol: {}
symbol: {},
cleanupdefs: false
});

var symbolAttrs = symbolAttributes(options.symbol);
var cleanupAttributes = [];
if (options.cleanup && typeof options.cleanup === 'boolean') {
// For backwards compatibility (introduced in 0.2.6).
cleanupAttributes = ['style'];
} else if (Array.isArray(options.cleanup)){
cleanupAttributes = options.cleanup;
}

this.files.forEach(function (file) {
var $resultDocument = cheerio.load('<svg><defs></defs></svg>', { lowerCaseAttributeNames : false }),
Expand All @@ -80,58 +77,81 @@ module.exports = function (grunt) {
}).map(function (filepath) {
var filename = path.basename(filepath, '.svg');
var contentStr = grunt.file.read(filepath);
var uniqueId = md5(contentStr);
var $ = cheerio.load(contentStr, {
normalizeWhitespace: true,
xmlMode: true
});

// Map to store references from id to uniqueId + id;
var mappedIds = {};

// Make IDs unique
$('[id]').each(function () {
// Remove empty g elements
$('g').each(function(){
var $elem = $(this);
var id = $elem.attr('id');
var newId = 'svgstore' + uniqueId + id;
mappedIds[id] = newId;
$elem.attr('id', newId);
if (!$elem.children().length) {
$elem.remove();
}
});

// Search for an url() reference in every attribute of every tag
// replace the id with the unique one.
// Map to store references from id to uniqueId + id;
// N.B.: only IDs that are referenced are mapped.
var mappedIds = {};
var uniqueId;
$('*').each(function () {
var $elem = $(this);
var attrs = $elem.attr();
Object.keys(attrs).forEach(function (key) {
var value = attrs[key];
var match;
while ((match = urlPattern.exec(value)) !== null) {
if (mappedIds[match[1]] !== undefined) {
value = value.replace(match[0], 'url(#' + mappedIds[match[1]] + ')');
} else {
grunt.log.warn('Can\'t reference to id "' + match[1] + '" from attribute "' + attr + '" in "' + this[0].name + '" because it is not defined.');
var refId = match[1];

// Add id mapping if not already present
if (!mappedIds[refId]) {
if (!uniqueId) {
uniqueId = md5(contentStr);
}
var newId = 'svgstore' + uniqueId + refId;
mappedIds[refId] = newId;
}

$elem.attr(key, value.replace(match[0], 'url(#' + mappedIds[refId] + ')'));
}
if (options.cleanup && key === 'style') {
value = null;

if (options.cleanupdefs || !$elem.parents('defs').length) {
if (cleanupAttributes.indexOf(key) > -1){
$elem.removeAttr(key);
}
}
$elem.attr(key, value);
});
});

// Apply ID mapping and remove unreferenced IDs
$('[id]').each(function () {
var $elem = $(this);
var id = $elem.attr('id');
var newId = mappedIds[id];
if (!newId) {
// ID is not refenced and can therefore be removed
$elem.removeAttr('id');
} else {
// Replace id by mapped id
$elem.attr('id', newId);
}
});

var $svg = $('svg');
var $title = $('title');
var $desc = $('desc');
var $def = $('defs').first();
var defContent = $def.length && $def.html();

// merge in the defs from this svg in the result defs block.
$resultDefs.append($def.html());
// Merge in the defs from this svg in the result defs block
if (defContent) {
$resultDefs.append(defContent);
}

var title = $title.first().html();
var desc = $desc.first().html();

// remove def, title, desc from this svg
// Remove def, title, desc from this svg
$def.remove();
$title.remove();
$desc.remove();
Expand All @@ -141,26 +161,37 @@ module.exports = function (grunt) {
// If there is no title use the filename
title = title || id;

var resultStr = '<symbol' + symbolAttrs + '>' + '<title>' + title + '</title>';
// Generate symbol
var $res = cheerio.load('<symbol>' + $svg.html() + '</symbol>', { lowerCaseAttributeNames: false });
var $symbol = $res('symbol').first();

// Only add desc if it was set
if ( desc ) { resultStr +='<desc>'+ desc +'</desc>'; }
// Merge in symbol attributes from option
for (var attr in options.symbol) {
$symbol.attr(attr, options.symbol[attr]);
}

resultStr += $svg.html() + '</symbol>';
// Add title and desc (if provided)
if (desc) {
$symbol.prepend('<desc>' + desc + '</desc>');
}

// Create a object
var $res = cheerio.load(resultStr, { lowerCaseAttributeNames : false });
if (title) {
$symbol.prepend('<title>' + title + '</title>');
}

$res('symbol').attr('viewBox', $svg.attr('viewBox'));
// Add viewBox (if present on SVG)
var viewBox = $svg.attr('viewBox');
if (viewBox) {
$symbol.attr('viewBox', viewBox);
}

// Add ID to symbol
var graphicId = options.prefix + id;
// Add ID to the first Element
$res('*').first().attr('id', graphicId);
$symbol.attr('id', graphicId);

// Append <symbol> to resulting SVG
$resultSvg.append($res.html());


// Add icon to the demo.html array
if (options.includedemo) {
iconNameViewBoxArray.push({
Expand All @@ -169,7 +200,7 @@ module.exports = function (grunt) {
}
});

// remove defs block if empty
// Remove defs block if empty
if ( $resultDefs.html().trim() === '' ) {
$resultDefs.remove();
}
Expand Down
2 changes: 1 addition & 1 deletion test/expected/cleanup.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions test/expected/cleanup_fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/expected/defs_clean.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/expected/defs_noclean.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading