Permalink
Browse files

Merge branch 'dev'

* dev:
  v0.4.0
  update dependencies
  moveGroupAttrsToElems now applies transforms to sub-groups & text.
  Fix issue with convertPathData when sequential h or l movements are in the opposite direction.
  Updated applyTransforms to optionally apply path transforms & convert the stroke-width when a line is scaled uniformly (ie, no skew).
  Updated mergePaths to join paths with the same attributes & values.
  Possible solution to issue #132 with test cases.
  • Loading branch information...
2 parents 6fa0704 + e767d7e commit 7e63266cb12dc566c4880255cc6768945659a628 @deepsweet deepsweet committed Nov 18, 2013
View
2 .svgo.yml
@@ -32,8 +32,8 @@ plugins:
- removeHiddenElems
- removeEmptyText
- moveElemsAttrsToGroup
- - collapseGroups
- moveGroupAttrsToElems
+ - collapseGroups
- convertPathData
- convertTransform
- removeEmptyAttrs
View
7 CHANGELOG.md
@@ -1,3 +1,6 @@
+### [ [>](https://github.com/svg/svgo/tree/v0.4.0) ] 0.4.0 / 18.11.2013
+* merge almost all pull-requests
+* update dependencies
### [ [>](https://github.com/svg/svgo/tree/v0.3.7) ] 0.3.7 / 24.06.2013
* do not remove `result` attribute from filter primitives (fix [#122](https://github.com/svg/svgo/issues/122))
@@ -167,9 +170,9 @@
* add [./docs/](https://github.com/svg/svgo/tree/master/docs)
* plugins/convertPathData: don't remove first `M` even if it's `0,0`
* plugins/convertPathData: stronger defense from infinite loop
-* plugins/moveElemsAttrsToGroup: should affect only inheritable attributes (fix [#46](https://github.com/svg/svgo/issues/46))*
+* plugins/moveElemsAttrsToGroup: should affect only inheritable attributes (fix [#46](https://github.com/svg/svgo/issues/46))*
* plugins/removeComments: ignore comments which starts with '!' (close [#43](https://github.com/svg/svgo/issues/43))
-* config: `cleanupAttrs` should be before `convertStyleToAttrs` (fix [#44](https://github.com/svg/svgo/issues/44))*
+* config: `cleanupAttrs` should be before `convertStyleToAttrs` (fix [#44](https://github.com/svg/svgo/issues/44))*
* lib/svgo/jsAPI: add `eachAttr()` optional context param
* temporarily remove PhantomJS and `--test` (close [#38](https://github.com/svg/svgo/issues/38))
* q@0.8.10 compatibility: 'end is deprecated, use done instead' fix
View
8 package.json
@@ -1,6 +1,6 @@
{
"name": "svgo",
- "version": "0.3.7",
+ "version": "0.4.0",
"description": "Nodejs-based tool for optimizing SVG vector graphics files",
"keywords": [ "svgo", "svg", "optimize", "minify" ],
"homepage": "http://svg.github.com/svgo/",
@@ -36,14 +36,14 @@
},
"dependencies": {
"sax": "~0.5.0",
- "coa": "~0.3.7",
+ "coa": "~0.4.0",
"js-yaml": "",
"colors": "~0.6.0",
"whet.extend": ""
},
"devDependencies": {
- "mocha": "~1.9.0",
- "should": "~1.2.0",
+ "mocha": "~1.14.0",
+ "should": "~2.1.0",
"istanbul": "~0.1.0",
"mocha-istanbul": "",
"coveralls": ""
View
117 plugins/_path.js
@@ -180,72 +180,103 @@ exports.relative2absolute = function(data) {
*
* @param {Object} elem current element
* @param {Array} path input path data
+ * @param {Boolean} applyTransformsStroked whether to apply transforms to stroked lines.
+ * @param {Number} floatPrecision precision (used for stroke width)
* @return {Array} output path data
*/
-exports.applyTransforms = function(elem, path) {
-
+exports.applyTransforms = function(elem, path, applyTransformsStroked, floatPrecision) {
// if there are no 'stroke' attr and 'a' segments
if (
- elem.hasAttr('transform') &&
- !elem.hasAttr('stroke') &&
- path.every(function(i) { return i.instruction !== 'a'; })
+ !elem.hasAttr('transform') ||
+ !path.every(function(i) { return i.instruction !== 'a'; })
) {
+ return path;
+ }
+ var matrix = transformsMultiply(transform2js(elem.attr('transform').value)),
+ newPoint, sx, sy, strokeWidth;
+
+ if (elem.hasAttr('stroke') || elem.hasAttr('stroke-width')){
+ if (!applyTransformsStroked){
+ return path;
+ }
+ if (matrix.name == 'matrix'){
+ sx = +Math.sqrt(matrix.data[0] * matrix.data[0] + matrix.data[1] * matrix.data[1]).toFixed(floatPrecision);
+ sy = +Math.sqrt(matrix.data[2] * matrix.data[2] + matrix.data[3] * matrix.data[3]).toFixed(floatPrecision);
+ } else if (matrix.name == 'scale'){
+ sx = +matrix.data[0].toFixed(floatPrecision);
+ sy = +matrix.data[1].toFixed(floatPrecision);
+ } else {
+ sx = 1;
+ sy = 1;
+ }
+
+ if (sx !== sy){
+ return path;
+ }
+ if (sx !== 1){
+ if (elem.hasAttr('stroke-width')){
+ elem.attrs['stroke-width'].value = elem.attrs['stroke-width'].value * sx;
+ } else {
+ elem.addAttr({
+ name: 'stroke-width',
+ prefix: '',
+ local: 'stroke-width',
+ value: sx
+ });
+ }
+ }
+ }
- var matrix = transformsMultiply(transform2js(elem.attr('transform').value)),
- newPoint;
-
- path.forEach(function(pathItem) {
-
- if (pathItem.data) {
+ path.forEach(function(pathItem) {
- // h -> l
- if (pathItem.instruction === 'h') {
+ if (pathItem.data) {
- pathItem.instruction = 'l';
- pathItem.data[1] = 0;
+ // h -> l
+ if (pathItem.instruction === 'h') {
- // v -> l
- } else if (pathItem.instruction === 'v') {
+ pathItem.instruction = 'l';
+ pathItem.data[1] = 0;
- pathItem.instruction = 'l';
- pathItem.data[1] = pathItem.data[0];
- pathItem.data[0] = 0;
+ // v -> l
+ } else if (pathItem.instruction === 'v') {
- }
+ pathItem.instruction = 'l';
+ pathItem.data[1] = pathItem.data[0];
+ pathItem.data[0] = 0;
- // if there is a translate() transform
- if (pathItem.instruction === 'M' &&
- (matrix.data[4] !== 0 ||
- matrix.data[5] !== 0)
- ) {
+ }
- // then apply it only to the first absoluted M
- newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]);
- pathItem.data[0] = newPoint[0];
- pathItem.data[1] = newPoint[1];
+ // if there is a translate() transform
+ if (pathItem.instruction === 'M' &&
+ (matrix.data[4] !== 0 ||
+ matrix.data[5] !== 0)
+ ) {
- // clear translate() data from transform matrix
- matrix.data[4] = 0;
- matrix.data[5] = 0;
+ // then apply it only to the first absoluted M
+ newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]);
+ pathItem.data[0] = newPoint[0];
+ pathItem.data[1] = newPoint[1];
- } else {
+ // clear translate() data from transform matrix
+ matrix.data[4] = 0;
+ matrix.data[5] = 0;
- for (var i = 0; i < pathItem.data.length; i += 2) {
- newPoint = transformPoint(matrix.data, pathItem.data[i], pathItem.data[i + 1]);
- pathItem.data[i] = newPoint[0];
- pathItem.data[i + 1] = newPoint[1];
- }
+ } else {
+ for (var i = 0; i < pathItem.data.length; i += 2) {
+ newPoint = transformPoint(matrix.data, pathItem.data[i], pathItem.data[i + 1]);
+ pathItem.data[i] = newPoint[0];
+ pathItem.data[i + 1] = newPoint[1];
}
}
- });
+ }
- // remove transform attr
- elem.removeAttr('transform');
+ });
- }
+ // remove transform attr
+ elem.removeAttr('transform');
return path;
View
2 plugins/collapseGroups.js
@@ -45,7 +45,7 @@ exports.fn = function(item) {
if (g.attrs && g.content.length === 1) {
var inner = g.content[0];
- if (inner.elem) {
+ if (inner.elem && !(g.hasAttr('transform') && g.hasAttr('clip-path') && inner.hasAttr('transform'))) {
g.eachAttr(function(attr) {
if (!inner.hasAttr(attr.name)) {
inner.addAttr(attr);
View
5 plugins/convertPathData.js
@@ -6,6 +6,7 @@ exports.active = true;
exports.params = {
applyTransforms: true,
+ applyTransformsStroked: true,
straightCurves: true,
lineShorthands: true,
curveSmoothShorthands: true,
@@ -52,7 +53,7 @@ exports.fn = function(item, params) {
data = convertToRelative(data);
if (params.applyTransforms) {
- data = applyTransforms(item, data);
+ data = applyTransforms(item, data, params.applyTransformsStroked, params.floatPrecision);
}
data = filters(data, params);
@@ -492,7 +493,7 @@ function collapseRepeated(path) {
item.instruction === prev.instruction
) {
// increase previous h or v data with current
- if (item.instruction === 'h' || item.instruction === 'v') {
+ if ((item.instruction === 'h' || item.instruction === 'v')&& (prev.data[0] >= 0) == (item.data[0] >= 0)) {
prev.data[0] += item.data[0];
// concat previous data with current
} else {
View
60 plugins/mergePaths.js
@@ -17,33 +17,79 @@ exports.fn = function(item) {
if (item.isElem() && !item.isEmpty()) {
var prevContentItem,
- delim = '';
+ delim = '',
+ prevContentItemKeys = null,
+ prevItemPathClosed = false,
+ contentItemKeys = null,
+ contentItemPathClosed = false,
+ equalData,
+ attrName;
item.content = item.content.filter(function(contentItem) {
// merge only <path d="...z" />
if (prevContentItem &&
prevContentItem.isElem('path') &&
prevContentItem.hasAttr('d') &&
- Object.keys(prevContentItem.attrs).length === 1 &&
- prevContentItem.attr('d').value.charAt(prevContentItem.attr('d').value.length - 1) === 'z' &&
contentItem.isElem('path') &&
- contentItem.hasAttr('d') &&
- Object.keys(contentItem.attrs).length === 1
+ contentItem.hasAttr('d')
) {
+
+ prevItemPathClosed = prevContentItem.attr('d').value.charAt(prevContentItem.attr('d').value.length-1) === 'z';
+ contentItemPathClosed = contentItem.attr('d').value.charAt(contentItem.attr('d').value.length-1) === 'z';
+
+ if (!prevItemPathClosed && contentItemPathClosed){
+ //console.log('Previous path not closed, current plath closed', prevContentItem.attr('d').value, contentItem.attr('d').value);
+ prevContentItem = contentItem;
+ prevContentItemKeys = null;
+ return true;
+ }
+
+ if (prevContentItemKeys === null){
+ prevContentItemKeys = Object.keys(prevContentItem.attrs);
+ }
+
+ contentItemKeys = Object.keys(contentItem.attrs);
+ if (contentItemKeys.length !== 1 || prevContentItemKeys.length !== 1){
+ if (contentItemKeys.length !== prevContentItemKeys.length){
+ prevContentItem = contentItem;
+ prevContentItemKeys = null;
+ return true;
+ }
+
+ equalData = true;
+ for(var i = 0, I = contentItemKeys.length; i < I; i++){
+ attrName = contentItemKeys[i];
+ if (attrName != 'd'){
+ if(typeof prevContentItem.attrs[attrName] === "undefined"){
+ equalData = false;
+ break;
+ } else if (prevContentItem.attrs[attrName].value !== contentItem.attrs[attrName].value){
+ equalData = false;
+ break;
+ }
+ }
+ }
+ if (!equalData){
+ prevContentItem = contentItem;
+ prevContentItemKeys = null;
+ return true;
+ }
+ }
// "zM", but "z m"
// looks like a FontForge parsing bug
if (contentItem.attr('d').value.charAt(0) === 'm') {
delim = ' ';
+ } else {
+ delim = ''; // reset delim from looping
}
prevContentItem.attr('d').value += delim + contentItem.attr('d').value;
-
return false;
}
prevContentItem = contentItem;
-
+ prevContentItemKeys = null;
return true;
});
View
7 plugins/moveGroupAttrsToElems.js
@@ -1,10 +1,13 @@
'use strict';
-exports.type = 'perItemReverse';
+exports.type = 'perItem';
exports.active = true;
-var pathElems = require('./_collections.js').pathElems;
+var pathElems = require('./_collections.js').pathElems.slice();
+
+pathElems.push('g');
+pathElems.push('text');
/**
* Move group attrs to the content elements.
View
2 plugins/transformsWithOnePath.js
@@ -300,7 +300,7 @@ exports.fn = function(data, params) {
value: transform
});
- path = applyTransforms(pathElem, pathElem.pathJS);
+ path = applyTransforms(pathElem, pathElem.pathJS, true, params.floatPrecision);
// transformed data rounding
path.forEach(function(pathItem) {
View
25 test/plugins/collapseGroups.08.svg
@@ -0,0 +1,25 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <clipPath id="a">
+ <path d="..."/>
+ </clipPath>
+ <g transform="matrix(0 -1.25 -1.25 0 100 100)" clip-path="url(#a)">
+ <g transform="scale(.2)">
+ <path d="..."/>
+ <path d="..."/>
+ </g>
+ </g>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <clipPath id="a">
+ <path d="..."/>
+ </clipPath>
+ <g transform="matrix(0 -1.25 -1.25 0 100 100)" clip-path="url(#a)">
+ <g transform="scale(.2)">
+ <path d="..."/>
+ <path d="..."/>
+ </g>
+ </g>
+</svg>
View
35 test/plugins/collapseGroups.09.svg
@@ -0,0 +1,35 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <clipPath id="a">
+ <path d="..."/>
+ </clipPath>
+ <clipPath id="b">
+ <path d="..."/>
+ </clipPath>
+ <g transform="matrix(0 -1.25 -1.25 0 100 100)" clip-path="url(#a)">
+ <g transform="scale(.2)">
+ <g>
+ <g clip-path="url(#b)">
+ <path d="..."/>
+ <path d="..."/>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <clipPath id="a">
+ <path d="..."/>
+ </clipPath>
+ <clipPath id="b">
+ <path d="..."/>
+ </clipPath>
+ <g transform="matrix(0 -1.25 -1.25 0 100 100)" clip-path="url(#a)">
+ <g clip-path="url(#b)" transform="scale(.2)">
+ <path d="..."/>
+ <path d="..."/>
+ </g>
+ </g>
+</svg>
View
6 test/plugins/convertPathData.11.svg
@@ -2,6 +2,9 @@
<path fill="red" transform="rotate(15) scale(.5) skewX(5) translate(200,100)" d="M100,200 300,400 H100 V300 C100,100 250,100 250,200 S400,300 400,200 Q400,50 600,300 T1000,300 z"/>
<path fill="red" stroke="red" transform="rotate(15) scale(.5) skewX(5) translate(200,100)" d="M100,200 300,400 H100 V300 C100,100 250,100 250,200 S400,300 400,200 Q400,50 600,300 T1000,300 z"/>
<path fill="red" stroke="red" transform="rotate(15) scale(.5) skewX(5) translate(200,100)" d="M100,200 300,400 H100 V300 C100,100 250,100 250,200 S400,300 400,200 Q400,50 600,300 T1000,300 a150,150 0 1,0 150,-150 z"/>
+ <path fill="red" stroke="red" transform="rotate(15) scale(.5) translate(200,100)" d="M100,200 300,400 H100 V300 C100,100 250,100 250,200 S400,300 400,200 Q400,50 600,300 T1000,300 z"/>
+ <path fill="red" stroke="red" transform="rotate(15) scale(1.5) translate(200,100)" d="M100,200 300,400 H100 V300 C100,100 250,100 250,200 S400,300 400,200 Q400,50 600,300 T1000,300 z"/>
+ <path fill="red" stroke="red" transform="rotate(15) scale(0.33) translate(200,100)" d="M100,200 300,400 H100 V300 C100,100 250,100 250,200 S400,300 400,200 Q400,50 600,300 T1000,300 z"/>
</svg>
@@@
@@ -10,4 +13,7 @@
<path fill="red" d="M118.8 186.9l79.2 124.6-96.6-25.8 8.7-49.4c17.4-98.8 89.85-79.45 81.15-30.05s63.75 68.75 72.45 19.35q13.05-74.1 87.9 75.2t193.2 51.6z"/>
<path fill="red" stroke="red" transform="rotate(15) scale(.5) skewX(5) translate(200,100)" d="M100 200l200 200h-200v-100c0-200 150-200 150-100s150 100 150 0q0-150 200 100t400 0z"/>
<path fill="red" stroke="red" transform="rotate(15) scale(.5) skewX(5) translate(200,100)" d="M100 200l200 200h-200v-100c0-200 150-200 150-100s150 100 150 0q0-150 200 100t400 0a150 150 0 1 0 150-150z"/>
+ <path fill="red" stroke="red" d="M106.2 183.6l70.8 122.4-96.6-25.8 12.9-48.3c25.8-96.6 98.25-77.25 85.35-28.95s59.55 67.65 72.45 19.35q19.35-72.45 83.7 74.1t193.2 51.6z" stroke-width="0.5"/>
+ <path fill="red" stroke="red" d="M318.3 551.1l212.2 367.4-289.8-77.6 38.8-144.9c77.6-289.8 294.95-231.6 256.15-86.7s178.55 203.1 217.35 58.2q58.2-217.35 251 222.5t579.6 155.2z" stroke-width="1.5"/>
+ <path fill="red" stroke="red" d="M70.2 121.2l46.8 80.8-63.8-17 8.5-31.9c17-63.8 64.85-51.05 56.35-19.15s39.35 44.65 47.85 12.75q12.75-47.85 55.3 48.9t127.6 34z" stroke-width="0.33"/>
</svg>
View
31 test/plugins/convertPathData.12.svg
@@ -0,0 +1,31 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <path d="M10 50h30h-30"/>
+ <path d="M10 50h-30h30"/>
+ <path d="M10 50h-30h-50"/>
+ <path d="M10 50h30h50"/>
+ <path d="M10 50v30v-30"/>
+ <path d="M10 50v-30v30"/>
+ <path d="M10 50v-30v-50"/>
+ <path d="M10 50v30v50"/>
+ <path d="M10 50L10 80L10 0"/>
+ <path d="M10 50L10 10L10 80"/>
+ <path d="M10 50L80 50L0 50"/>
+ <path d="M10 50L0 50L80 50"/>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <path d="M10 50h30-30"/>
+ <path d="M10 50h-30 30"/>
+ <path d="M10 50h-80"/>
+ <path d="M10 50h80"/>
+ <path d="M10 50v30-30"/>
+ <path d="M10 50v-30 30"/>
+ <path d="M10 50v-80"/>
+ <path d="M10 50v80"/>
+ <path d="M10 50v30-80"/>
+ <path d="M10 50v-40 70"/>
+ <path d="M10 50h70-80"/>
+ <path d="M10 50h-10 80"/>
+</svg>
View
27 test/plugins/mergePaths.02.svg
@@ -0,0 +1,27 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <path d="M 0,0 z" fill="#fff" stroke="#333"/>
+ <path d="M 10,10 z" fill="#fff" stroke="#333"/>
+ <path d="M 20,20" fill="#fff" stroke="#333"/>
+ <path d="M 30,30 z" fill="#fff" stroke="#333"/>
+ <path d="M 30,30 z" fill="#f00"/>
+ <path d="M 40,40 z"/>
+ <path d="m 50,50 z"/>
+ <path d="M 40,40"/>
+ <path d="m 50,50"/>
+ <path d="M 40,40 z" fill="#fff" stroke="#333"/>
+ <path d="m 50,50 z" fill="#fff" stroke="#333"/>
+ <path d="M 40,40" fill="#fff" stroke="#333"/>
+ <path d="m 50,50" fill="#fff" stroke="#333"/>
+ <path d="m 50,50 z" fill="#fff" stroke="#333"/>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <path d="M 0,0 zM 10,10 zM 20,20" fill="#fff" stroke="#333"/>
+ <path d="M 30,30 z" fill="#fff" stroke="#333"/>
+ <path d="M 30,30 z" fill="#f00"/>
+ <path d="M 40,40 z m 50,50 zM 40,40 m 50,50"/>
+ <path d="M 40,40 z m 50,50 zM 40,40 m 50,50" fill="#fff" stroke="#333"/>
+ <path d="m 50,50 z" fill="#fff" stroke="#333"/>
+</svg>
View
19 test/plugins/moveGroupAttrsToElems.03.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <g transform="rotate(30)">
+ <g transform="scale(2)">
+ <path d="M0,0 L10,20"/>
+ <path d="M0,10 L20,30"/>
+ </g>
+ </g>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <g>
+ <g>
+ <path d="M0,0 L10,20" transform="rotate(30) scale(2)"/>
+ <path d="M0,10 L20,30" transform="rotate(30) scale(2)"/>
+ </g>
+ </g>
+</svg>
View
25 test/plugins/moveGroupAttrsToElems.04.svg
@@ -0,0 +1,25 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <g transform="rotate(30)">
+ <g>
+ <g transform="scale(2)">
+ <path d="M0,0 L10,20"/>
+ <path d="M0,10 L20,30"/>
+ </g>
+ </g>
+ <path d="M0,10 L20,30"/>
+ </g>
+</svg>
+
+@@@
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <g>
+ <g>
+ <g>
+ <path d="M0,0 L10,20" transform="rotate(30) scale(2)"/>
+ <path d="M0,10 L20,30" transform="rotate(30) scale(2)"/>
+ </g>
+ </g>
+ <path d="M0,10 L20,30" transform="rotate(30)"/>
+ </g>
+</svg>

0 comments on commit 7e63266

Please sign in to comment.