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

inline definitions #976

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .svgo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ plugins:
# param1: 1
# param2: 2

- inlineDefs: true
- removeDoctype
- removeXMLProcInst
- removeComments
Expand Down
228 changes: 228 additions & 0 deletions plugins/inlineDefs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
'use strict';

var JSAPI = require('../lib/svgo/jsAPI.js');

exports.type = 'full';
exports.active = true;
exports.description = 'inlines svg definitions';

/**
* plugin options
* @typedef {{onlyUnique: boolean}} Params
*/
exports.params = {
onlyUnique: true
};


/**
* replaces use tag with the corresponding definitions
* if onlyUnique = true, replaces only use tags with definitions referred to only once
* @param {Object} document
* @param {Params} params
* @returns {Object}
*/
exports.fn = function(document, params) {

var defs = document.querySelector('defs');
var uses = document.querySelectorAll( 'use' );

if(!uses) {
return document;
}

var useCount = _countUses(uses);

for (var i = 0; i < uses.length; i++) {
var hrefItem = uses[i].attr('xlink:href');
if (!hrefItem) {
hrefItem = uses[i].attr('href');
}
var href = hrefItem.value;

if (params.onlyUnique === true && useCount[href] > 1) {
continue;
}

var x = uses[i].hasAttr('x') ? uses[i].attr('x').value : null;
var y = uses[i].hasAttr('y') ? uses[i].attr('y').value : null;

var attrValue = null;
if (x && y) {
attrValue = 'translate(' + x + ', ' + y + ')';
} else if (x) {
attrValue = 'translate(' + x + ')';
}

var def = _findById(defs, href.match(idRegex)[1]);
if (!def) {
continue;
}
if (params.onlyUnique === true && useCount[href] === 1) {
def = _replaceElement(def);
}

for (var key in uses[i].attrs) {
if (uses[i].attrs.hasOwnProperty(key) && key !== 'x' && key !== 'y' && key !== 'xlink:href' && key !== 'href') {
def.addAttr(uses[i].attrs[key]);
}
}

if (attrValue) {
var g = new JSAPI({
elem: 'g',
attrs: {
transform: {
name: 'transform',
value: attrValue,
prefix: null,
local: 'transform'
}
},
content: [def]
});
_replaceElement(uses[i], g);
}
else {
_replaceElement(uses[i], def);
}

}

if (params.onlyUnique === false) {
for (var element in useCount) {
if (useCount.hasOwnProperty(element) && useCount[element] > 1) {
var tags = document.querySelectorAll(element);
for (var j = 0; j < tags.length; j++) {
tags[j].removeAttr('id');
}
}
}
}


_removeDefs(document, params);


return document;

};

/**
* removes defs tag from the svg if possible
* @param {Object} document
* @param {Object} params
* @private
*/
function _removeDefs(document, params) {
if (params.onlyUnique === false || document.querySelector('defs').content.length === 0) {
_replaceElement(document.querySelector('defs'));
}
}

/**
* counts the number of uses of definitions
* @param {Array} elements
* @returns {Object<string, number>}
* @private
*/
function _countUses(elements) {

return elements.reduce(function(result, item) {

var hrefItem = item.attr('xlink:href');
if (!hrefItem) {
hrefItem = item.attr('href');
}
var href = hrefItem.value;

if (result.hasOwnProperty(href)) {
result[href]++;
}
else {
result[href] = 1;
}

return result;

}, {});

}

/**
* replace element with another
* if newElement is omitted, oldElement will be removed without replacement
* @param {Object} oldElement
* @param {Object} [newElement]
* @returns {Object}
* @private
*/
function _replaceElement(oldElement, newElement) {

var elementIndex = _getElementIndex(oldElement);

if (newElement) {
oldElement.parentNode.spliceContent(elementIndex, 1, newElement);
} else {
oldElement.parentNode.spliceContent(elementIndex, 1);
}

return oldElement;

}

/**
* returns index of the element in the list of siblings
* returns -1 if element could not be found
* @param {Object} element
* @returns {number}
* @private
*/
function _getElementIndex(element) {

element.addAttr({
name: 'data-defs-index',
value: 'true',
prefix: '',
local: 'data-defs-index'
});

var index = element.parentNode.content.findIndex(function(contentElement) {

return contentElement.hasAttr('data-defs-index', 'true');

});

element.removeAttr('data-defs-index');

return index;

}

var idRegex = /^#?(\S+)/;

/**
* finds the first appearance of element with id = "id"
* (querySelector does not seam to handle multiple id ta
* @param {Object} element
* @param {string} id
* @returns {Object|null}
* @private
*/
function _findById(element, id) {

if (element.hasAttr('id', id)) {
return element;
}

if (element.content) {
for (var i = 0; i < element.content.length; i++) {
var result = _findById(element.content[i], id);
if (result !== null) {
return result;
}
}
}

return null;
}
2 changes: 1 addition & 1 deletion test/coa/testSvg/test.1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/coa/testSvg/test.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/plugins/inlineDefs.01.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions test/plugins/inlineDefs.02.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions test/plugins/inlineDefs.03.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions test/plugins/inlineDefs.04.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions test/plugins/inlineDefs.05.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions test/plugins/inlineDefs.06.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions test/plugins/inlineDefs.07.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/plugins/inlineDefs.08.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions test/plugins/inlineDefs.09.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading