Comparing version 0.0.9 to 0.1.0
@@ -0,1 +1,29 @@ | ||
### [ [>](//github.com/svg/svgo/tree/v0.1.0) ] 0.1.0 / 22.11.2012 | ||
* new plugin [plugins/removeUnknownsAndDefaults](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) (close [#6](https://github.com/svg/svgo/issues/6)) | ||
* plugins/convertPathData: convert straight curves into lines segments (close [#17](https://github.com/svg/svgo/issues/17)); remove an absolute coords conversions | ||
* plugins/convertPathData: convert quadratic Bézier curveto into smooth shorthand (close [#31](https://github.com/svg/svgo/issues/31)) | ||
* plugins/convertPathData: convert curveto into smooth shorthand (close [#30](https://github.com/svg/svgo/issues/30)) | ||
* lib/svgo: global API refactoring (close [#37](https://github.com/svg/svgo/issues/37)) | ||
* lib/svgo: fatal and stupid error in stream chunks concatenation (fix [#40](https://github.com/svg/svgo/issues/40)) | ||
* lib/coa: batch folder optimization (close [#29](https://github.com/svg/svgo/issues/29)) | ||
* lib/coa: support arguments as aliases to `--input` and `--output` (close [#28](https://github.com/svg/svgo/issues/28)) | ||
* project logo by [Egor Bolhshakov](http://xizzzy.ru/) | ||
* move modules to `./lib/svgo/` | ||
* rename and convert `config.json` to `.svgo.yml` | ||
* 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/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))* | ||
* 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 | ||
* add [Istanbul](https://github.com/gotwarlost/istanbul) code coverage | ||
* update dependencies versions and gitignore | ||
* README: add TODO section with versions milestones | ||
* update README with License section | ||
* update LICENSE with russian translation | ||
* `.editorconfig`: 2 spaces for YAML | ||
### [ [>](//github.com/svg/svgo/tree/v0.0.9) ] 0.0.9 / 29.10.2012 | ||
@@ -10,3 +38,3 @@ * [plugins how-to](https://github.com/svg/svgo/tree/master/plugins#readme) (close [#27](https://github.com/svg/svgo/issues/27)) | ||
* plugins/convertPathData: do not remove very first M from the path data (fix [#24](https://github.com/svg/svgo/issues/24)) | ||
* plugins/convertPathData: optimize path data in <glyph> and <missing-glyph> (close [#20](https://github.com/svg/svgo/issues/20)) | ||
* plugins/convertPathData: optimize path data in <glyph> and <missing-glyph> (close [#20](https://github.com/svg/svgo/issues/20)) | ||
* plugins/convertTransform: add patternTransform attribute to the process (close [#15](https://github.com/svg/svgo/issues/15)) | ||
@@ -13,0 +41,0 @@ * plugins/convertTransform: Firefox: removing extra space in front of negative number is alowed only in path data, but not in transform (fix [#12](https://github.com/svg/svgo/issues/12)) |
@@ -14,6 +14,9 @@ /** | ||
var INHERIT = require('inherit'), | ||
CONFIG = require('./config'), | ||
SVG2JS = require('./svg2js'), | ||
PLUGINS = require('./plugins'), | ||
JS2SVG = require('./js2svg'); | ||
Q = require('q'), | ||
FS = require('fs'), | ||
CONFIG = require('./svgo/config'), | ||
SVG2JS = require('./svgo/svg2js'), | ||
PLUGINS = require('./svgo/plugins'), | ||
JS2SVG = require('./svgo/js2svg'), | ||
decodeSVGDatauri = require('./svgo/tools').decodeSVGDatauri; | ||
@@ -26,3 +29,3 @@ /** | ||
/** | ||
* @param {Object} [config] config to extend | ||
* @param {Object} [config] custom config to extend default | ||
* | ||
@@ -40,18 +43,25 @@ * @constructs | ||
/** | ||
* Main optimize function. | ||
* Optimize SVG data from string. | ||
* | ||
* @param {String} svgdata input data | ||
* @param {String} str input string | ||
* | ||
* @return {String} output data deferred promise | ||
* @return {Object} output string deferred promise | ||
*/ | ||
optimize: function(svgdata) { | ||
fromString: function(str) { | ||
str = decodeSVGDatauri(str); | ||
return this.config | ||
.then(function(config) { | ||
return SVG2JS(svgdata, config.svg2js) | ||
return SVG2JS(str, config.svg2js) | ||
.then(function(jsdata) { | ||
return JS2SVG(PLUGINS(jsdata, config.plugins), config.js2svg); | ||
var out = JS2SVG(PLUGINS(jsdata, config.plugins), config.js2svg); | ||
out.info.startBytes = Buffer.byteLength(str, 'utf-8'); | ||
out.info.endBytes = Buffer.byteLength(out.data, 'utf-8'); | ||
return out; | ||
}); | ||
@@ -61,4 +71,50 @@ | ||
}, | ||
/** | ||
* Optimize SVG data from Stream. | ||
* | ||
* @param {Object} stream input stream | ||
* | ||
* @return {Object} output string deferred promise | ||
*/ | ||
fromStream: function(stream) { | ||
var deferred = Q.defer(), | ||
inputData = '', | ||
self = this; | ||
stream.pause(); | ||
stream | ||
.on('data', function(chunk) { | ||
inputData += chunk; | ||
}) | ||
.once('end', function() { | ||
deferred.resolve(inputData); | ||
}) | ||
.resume(); | ||
return deferred.promise | ||
.then(function(str) { | ||
return self.fromString(str); | ||
}); | ||
}, | ||
/** | ||
* Optimize SVG data from file. | ||
* | ||
* @param {String} path file path | ||
* | ||
* @return {Object} output string deferred promise | ||
*/ | ||
fromFile: function(path) { | ||
return this.fromStream(FS.createReadStream(path, { encoding: 'utf8' })); | ||
} | ||
}); |
{ | ||
"name": "svgo", | ||
"version": "0.0.9", | ||
"version": "0.1.0", | ||
"description": "Nodejs-based tool for optimizing SVG vector graphics files", | ||
"keywords": [ | ||
"svgo", | ||
"svg", | ||
"optimize", | ||
"minify" | ||
], | ||
"keywords": [ "svgo", "svg", "optimize", "minify" ], | ||
"homepage": "http://svg.github.com/svgo/", | ||
"bugs": { | ||
"url": "https://github.com/svg/svgo/issues" | ||
"url": "https://github.com/svg/svgo/issues", | ||
"email": "svgo@soulshine.in" | ||
}, | ||
"author": "Kir Belevich <kir@soulshine.in> (https://github.com/deepsweet)", | ||
"contributors": [ | ||
"Sergey Belov <peimei@ya.ru> (http://github.com/arikon)" | ||
], | ||
"author": { | ||
"name": "Kir Belevich", | ||
"email": "kir@soulshine.in", | ||
"url": "https://github.com/deepsweet" | ||
}, | ||
"contributors": [{ | ||
"name": "Sergey Belov", | ||
"email": "peimei@ya.ru", | ||
"url": "http://github.com/arikon" | ||
}], | ||
"repository": { | ||
@@ -24,23 +26,28 @@ "type": "git", | ||
"main": "./lib/svgo.js", | ||
"bin": { | ||
"svgo": "./bin/svgo" | ||
}, | ||
"directories": { | ||
"bin": "./bin", | ||
"lib": "./lib" | ||
"lib": "./lib", | ||
"example": "./examples" | ||
}, | ||
"bin": { | ||
"svgo": "./bin/svgo" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha --reporter spec --require should --recursive" | ||
"test": "./node_modules/.bin/mocha --reporter spec --require should --recursive", | ||
"cover": "./node_modules/.bin/istanbul instrument --output lib-cov --no-compact --variable global.__coverage__ lib && ./node_modules/.bin/mocha --reporter mocha-istanbul --require should test/config test/svg2js" | ||
}, | ||
"dependencies": { | ||
"sax": "0.4.x", | ||
"q": "0.8.x", | ||
"q-fs": "", | ||
"coa": "0.3.x", | ||
"sax": "~0.4", | ||
"q": "~0.8.10", | ||
"q-fs": "~0.1", | ||
"coa": "~0.3.7", | ||
"inherit": "", | ||
"node.extend": "" | ||
"node.extend": "", | ||
"yamljs": "~0.1.3" | ||
}, | ||
"devDependencies": { | ||
"mocha": "1.4.x", | ||
"should": "" | ||
"mocha": "~1.6", | ||
"should": "~1", | ||
"istanbul": "~0.1", | ||
"mocha-istanbul": "" | ||
}, | ||
@@ -50,8 +57,6 @@ "engines": { | ||
}, | ||
"licenses": [ | ||
{ | ||
"licenses": [{ | ||
"type": "MIT", | ||
"url": "https://raw.github.com/svg/svgo/master/LICENSE" | ||
} | ||
] | ||
}] | ||
} |
@@ -0,1 +1,820 @@ | ||
// http://www.w3.org/TR/SVG/intro.html#Definitions | ||
var elemsGroups = exports.elemsGroups = { | ||
animation: ['animate', 'animateColor', 'animateMotion', 'animateTransform', 'set'], | ||
descriptive: ['desc', 'metadata', 'title'], | ||
shape: ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect'], | ||
structural: ['defs', 'g', 'svg', 'symbol', 'use'], | ||
gradient: ['linearGradient', 'radialGradient'], | ||
container: ['a', 'defs', 'glyph', 'g', 'marker', 'mask', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol'] | ||
}; | ||
// var defaults = exports.defaults = { | ||
// 'externalResourcesRequired': 'false', | ||
// 'xlink:type': 'simple' | ||
// }; | ||
// http://www.w3.org/TR/SVG/intro.html#Definitions | ||
var attrsGroups = exports.attrsGroups = { | ||
animationAddition: ['additive', 'accumulate'], | ||
animationAttributeTarget: ['attributeType', 'attributeName'], | ||
animationEvent: ['onbegin', 'onend', 'onrepeat', 'onload'], | ||
animationTiming: ['begin', 'dur', 'end', 'min', 'max', 'restart', 'repeatCount', 'repeatDur', 'fill'], | ||
animationValue: ['calcMode', 'values', 'keyTimes', 'keySplines', 'from', 'to', 'by'], | ||
conditionalProcessing: ['requiredFeatures', 'requiredExtensions', 'systemLanguage'], | ||
core: ['id', 'xml:base', 'xml:lang', 'xml:space'], | ||
graphicalEvent: ['onfocusin', 'onfocusout', 'onactivate', 'onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onload'], | ||
presentation: ['alignment-baseline', 'baseline-shift', 'clip', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'display', 'dominant-baseline', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'overflow', 'pointer-events', 'shape-rendering', 'stop-color', 'stop-opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'unicode-bidi', 'visibility', 'word-spacing', 'writing-mode'], | ||
xlink: ['xlink:href', 'xlink:show', 'xlink:actuate', 'xlink:type', 'xlink:role', 'xlink:arcrole', 'xlink:title'], | ||
documentEvent: ['onunload', 'onabort', 'onerror', 'onresize', 'onscroll', 'onzoom'], | ||
filterPrimitive: ['x', 'y', 'width', 'height'], | ||
transferFunction: ['type', 'tableValues', 'slope', 'intercept', 'amplitude', 'exponent', 'offset'] | ||
}; | ||
var groupDefaults = exports.groupDefaults = { | ||
filterPrimitive: {x: '0', y: '0', width: '100%', height: '100%'}, | ||
transferFunction: {slope: '1', intercept: '0', amplitude: '1', exponent: '1', offset: '0'} | ||
}; | ||
// http://www.w3.org/TR/SVG/eltindex.html | ||
exports.elems = { | ||
a: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
attrsGroups.xlink, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'target' | ||
], | ||
defaults: { | ||
target: '_self' | ||
}, | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive, | ||
elemsGroups.shape, | ||
elemsGroups.structural, | ||
elemsGroups.gradient, | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
altGlyph: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
attrsGroups.xlink, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x', | ||
'y', | ||
'dx', | ||
'dy', | ||
'glyphRef', | ||
'format', | ||
'rotate' | ||
], | ||
content: [] | ||
}, | ||
altGlyphDef: { | ||
attrs: [attrsGroups.core], | ||
content: ['glyphRef'] | ||
}, | ||
altGlyphItem: { | ||
attrs: [attrsGroups.core], | ||
content: [ | ||
'glyphRef', | ||
'altGlyphItem' | ||
] | ||
}, | ||
animate: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.animationAddition, | ||
attrsGroups.animationAttributeTarget, | ||
attrsGroups.animationEvent, | ||
attrsGroups.animationTiming, | ||
attrsGroups.animationValue, | ||
attrsGroups.presentation, | ||
attrsGroups.xlink, | ||
'externalResourcesRequired' | ||
], | ||
content: [elemsGroups.descriptive] | ||
}, | ||
animateColor: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.animationEvent, | ||
attrsGroups.xlink, | ||
attrsGroups.animationAttributeTarget, | ||
attrsGroups.animationTiming, | ||
attrsGroups.animationValue, | ||
attrsGroups.animationAddition, | ||
attrsGroups.presentation, | ||
'externalResourcesRequired' | ||
], | ||
content: [elemsGroups.descriptive] | ||
}, | ||
animateMotion: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.animationEvent, | ||
attrsGroups.xlink, | ||
attrsGroups.animationTiming, | ||
attrsGroups.animationValue, | ||
attrsGroups.animationAddition, | ||
'externalResourcesRequired', | ||
'path', | ||
'keyPoints', | ||
'rotate', | ||
'origin' | ||
], | ||
defaults: { | ||
'rotate': '0' | ||
}, | ||
content: [ | ||
elemsGroups.descriptive, | ||
'mpath' | ||
] | ||
}, | ||
animateTransform: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.animationEvent, | ||
attrsGroups.xlink, | ||
attrsGroups.animationAttributeTarget, | ||
attrsGroups.animationTiming, | ||
attrsGroups.animationValue, | ||
attrsGroups.animationAddition, | ||
'externalResourcesRequired', | ||
'type' | ||
], | ||
defaults: { | ||
type: 'translate' | ||
}, | ||
content: [elemsGroups.descriptive] | ||
}, | ||
circle: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'cx', | ||
'cy', | ||
'r' | ||
], | ||
defaults: { | ||
cx: 0, | ||
cy: 0 | ||
}, | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive | ||
] | ||
}, | ||
clipPath: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'clipPathUnits' | ||
], | ||
defaults: { | ||
clipPathUnits: 'userSpaceOnUse' | ||
}, | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive, | ||
elemsGroups.shape, | ||
'text', | ||
'use' | ||
] | ||
}, | ||
'color-profile': { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.xlink, | ||
'local', | ||
'name', | ||
'rendering-intent' | ||
], | ||
defaults: { | ||
name: 'sRGB', | ||
'rendering-intent': 'auto' | ||
}, | ||
content: [elemsGroups.descriptive] | ||
}, | ||
cursor: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.xlink, | ||
'externalResourcesRequired', | ||
'x', | ||
'y' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0' | ||
}, | ||
content: [elemsGroups.descriptive] | ||
}, | ||
defs: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform' | ||
], | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive, | ||
elemsGroups.shape, | ||
elemsGroups.structural, | ||
elemsGroups.gradient, | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
desc: { | ||
attrs: [ | ||
attrsGroups.core, | ||
'class', | ||
'style' | ||
] | ||
}, | ||
ellipse: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'cx', | ||
'cy', | ||
'rx', | ||
'ry' | ||
], | ||
defaults: { | ||
cx: '0', | ||
cy: '0' | ||
}, | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive | ||
] | ||
}, | ||
feBlend: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
// TODO: in - 'If no value is provided and this is the first filter primitive, | ||
// then this filter primitive will use SourceGraphic as its input' | ||
'in', | ||
'in2', | ||
'mode' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
defaults: { | ||
mode: 'normal' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
}, | ||
feColorMatrix: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
'in', | ||
'type', | ||
'values' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
defaults: { | ||
type: 'matrix' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
}, | ||
feComponentTransfer: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
'in' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
content: [ | ||
'feFuncA', | ||
'feFuncB', | ||
'feFuncG', | ||
'feFuncR' | ||
] | ||
}, | ||
feComposite: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
'in', | ||
'in2', | ||
'operator', | ||
'k1', | ||
'k2', | ||
'k3', | ||
'k4' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
defaults: { | ||
operator: 'over', | ||
k1: '0', | ||
k2: '0', | ||
k3: '0', | ||
k4: '0' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
}, | ||
feConvolveMatrix: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
'in', | ||
'order', | ||
'kernelMatrix', | ||
// TODO: divisor - 'The default value is the sum of all values in kernelMatrix, | ||
// with the exception that if the sum is zero, then the divisor is set to 1' | ||
'divisor', | ||
'bias', | ||
// TODO: targetX - 'By default, the convolution matrix is centered in X over each | ||
// pixel of the input image (i.e., targetX = floor ( orderX / 2 ))' | ||
'targetX', | ||
'targetY', | ||
'edgeMode', | ||
// TODO: kernelUnitLength - 'The first number is the <dx> value. The second number | ||
// is the <dy> value. If the <dy> value is not specified, it defaults to the same value as <dx>' | ||
'kernelUnitLength', | ||
'preserveAlpha' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
defaults: { | ||
order: '3', | ||
bias: '0', | ||
edgeMode: 'duplicate', | ||
preserveAlpha: 'false' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
}, | ||
feDiffuseLighting: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
'in', | ||
'surfaceScale', | ||
'diffuseConstant', | ||
'kernelUnitLength' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
defaults: { | ||
surfaceScale: '1', | ||
diffuseConstant: '1' | ||
}, | ||
content: [ | ||
elemsGroups.descriptive, | ||
// TODO: 'exactly one light source element, in any order' | ||
'feDistantLight', | ||
'fePointLight', | ||
'feSpotLight' | ||
] | ||
}, | ||
feDisplacementMap: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
'in', | ||
'in2', | ||
'scale', | ||
'xChannelSelector', | ||
'yChannelSelector' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
defaults: { | ||
scale: '0', | ||
xChannelSelector: 'A', | ||
yChannelSelector: 'A' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
}, | ||
feDistantLight: { | ||
attrs: [ | ||
attrsGroups.core, | ||
'azimuth', | ||
'elevation' | ||
], | ||
defaults: { | ||
azimuth: '0', | ||
elevation: '0' | ||
}, | ||
content: [ | ||
'animate', | ||
'set' | ||
] | ||
}, | ||
feFlood: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
content: [ | ||
'animate', | ||
'animateColor', | ||
'set' | ||
] | ||
}, | ||
feFuncA: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.transferFunction | ||
], | ||
groupDefaults: groupDefaults.transferFunction, | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
}, | ||
feFuncB: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.transferFunction | ||
], | ||
groupDefaults: groupDefaults.transferFunction, | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
}, | ||
feFuncG: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.transferFunction | ||
], | ||
groupDefaults: groupDefaults.transferFunction, | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
}, | ||
feFuncR: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.transferFunction | ||
], | ||
groupDefaults: groupDefaults.transferFunction, | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
}, | ||
feGaussianBlur: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.filterPrimitive, | ||
'class', | ||
'style', | ||
'in', | ||
'stdDeviation' | ||
], | ||
groupDefaults: groupDefaults.filterPrimitive, | ||
defaults: { | ||
stdDeviation: '0' | ||
}, | ||
content: [ | ||
'set', | ||
'animate' | ||
] | ||
}, | ||
feImage: {}, | ||
feMerge: {}, | ||
feMergeNode: {}, | ||
feMorphology: {}, | ||
feOffset: {}, | ||
fePointLight: {}, | ||
feSpecularLighting: {}, | ||
feSpotLight: {}, | ||
feTile: {}, | ||
feTurbulence: {}, | ||
filter: {}, | ||
font: {}, | ||
'font-face': {}, | ||
'font-face-format': {}, | ||
'font-face-name': {}, | ||
'font-face-src': {}, | ||
'font-face-uri': {}, | ||
foreignObject: {}, | ||
g: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform' | ||
], | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive, | ||
elemsGroups.shape, | ||
elemsGroups.structural, | ||
elemsGroups.gradient, | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
glyph: {}, | ||
glyphRef: {}, | ||
hkern: {}, | ||
image: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.xlink, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'preserveAspectRatio', | ||
'transform', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
preserveAspectRatio: 'xMidYMid meet' | ||
}, | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive | ||
] | ||
}, | ||
line: {}, | ||
linearGradient: { | ||
attrs: [ | ||
attrsGroups.core, | ||
attrsGroups.presentation, | ||
attrsGroups.xlink, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'x1', | ||
'y1', | ||
'x2', | ||
'y2', | ||
'gradientUnits', | ||
'gradientTransform', | ||
'spreadMethod', | ||
'xlink:href' | ||
], | ||
defaults: { | ||
x1: '0', | ||
y1: '0', | ||
x2: '100%', | ||
y2: '0', | ||
spreadMethod: 'pad' | ||
}, | ||
content: [ | ||
elemsGroups.descriptive, | ||
'animate', | ||
'animateTransform', | ||
'set', | ||
'stop' | ||
] | ||
}, | ||
marker: {}, | ||
mask: {}, | ||
metadata: {}, | ||
'missing-glyph': {}, | ||
mpath: {}, | ||
path: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'externalResourcesRequired', | ||
'transform', | ||
'd', | ||
'pathLength' | ||
], | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive | ||
] | ||
}, | ||
pattern: {}, | ||
polygon: {}, | ||
polyline: {}, | ||
radialGradient: { | ||
defaults: { | ||
cx: '50%', | ||
cy: '50%', | ||
r: '50%' | ||
} | ||
}, | ||
rect: {}, | ||
script: {}, | ||
set: {}, | ||
stop: {}, | ||
style: {}, | ||
svg: { | ||
attrs: [ | ||
attrsGroups.conditionalProcessing, | ||
attrsGroups.core, | ||
attrsGroups.documentEvent, | ||
attrsGroups.graphicalEvent, | ||
attrsGroups.presentation, | ||
'class', | ||
'style', | ||
'x', | ||
'y', | ||
'width', | ||
'height', | ||
'viewBox', | ||
'preserveAspectRatio', | ||
'zoomAndPan', | ||
'version', | ||
'baseProfile', | ||
'contentScriptType', | ||
'contentStyleType' | ||
], | ||
defaults: { | ||
x: '0', | ||
y: '0', | ||
width: '100%', | ||
height: '100%', | ||
preserveAspectRatio: 'xMidYMid meet', | ||
zoomAndPan: 'magnify', | ||
version: '1.1', | ||
baseProfile: 'none', | ||
contentScriptType: 'application/ecmascript', | ||
contentStyleType: 'text/css' | ||
}, | ||
content: [ | ||
elemsGroups.animation, | ||
elemsGroups.descriptive, | ||
elemsGroups.shape, | ||
elemsGroups.structural, | ||
elemsGroups.gradient, | ||
'a', | ||
'altGlyphDef', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'foreignObject', | ||
'image', | ||
'marker', | ||
'mask', | ||
'pattern', | ||
'script', | ||
'style', | ||
'switch', | ||
'text', | ||
'view' | ||
] | ||
}, | ||
switch: {}, | ||
symbol: {}, | ||
text: {}, | ||
textPath: {}, | ||
title: {}, | ||
tref: {}, | ||
tspan: {}, | ||
use: {}, | ||
view: {}, | ||
vkern: {} | ||
}; | ||
// http://wiki.inkscape.org/wiki/index.php/Inkscape-specific_XML_attributes | ||
@@ -82,28 +901,24 @@ exports.editorNamespaces = [ | ||
// http://www.w3.org/TR/SVG/intro.html#Definitions | ||
var elems = exports.elems = { | ||
'animation': ['animate', 'animateColor', 'animateMotion', 'animateTransform', 'set'], | ||
'descriptive': ['desc', 'metadata', 'title'], | ||
'shape': ['circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect'], | ||
'structural': ['defs', 'g', 'svg', 'symbol', 'use'], | ||
'gradient': ['linearGradient', 'radialGradient'], | ||
// http://www.w3.org/TR/SVG/intro.html#TermContainerElement | ||
'container': ['a', 'defs', 'glyph', 'g', 'marker', 'mask', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol'] | ||
}; | ||
// http://www.w3.org/TR/SVG/propidx.html | ||
exports.nonInheritedAttrs = [ | ||
'alignment-baseline', | ||
'baseline-shift', | ||
'clip', | ||
'clip-path', | ||
'display', | ||
'dominant-baseline', | ||
'enable-background', | ||
'filter', | ||
'flood-color', | ||
'flood-opacity', | ||
'lighting-color', | ||
'mask', | ||
'opacity', | ||
'overflow', | ||
'stop-color', | ||
'stop-opacity', | ||
'text-decoration', // TODO: "see prose" | ||
'unicode-bidi' | ||
]; | ||
// http://www.w3.org/TR/SVG/intro.html#Definitions | ||
var attrs = exports.attrs = { | ||
'animationAddition': ['additive', 'accumulate'], | ||
'animationAttributeTarget': ['attributeType', 'attributeName'], | ||
'animationEvent': ['onbegin', 'onend', 'onrepeat', 'onload'], | ||
'animationTiming': ['begin', 'dur', 'end', 'min', 'max', 'restart', 'repeatCount', 'repeatDur', 'fill'], | ||
'animationValue': ['calcMode', 'values', 'keyTimes', 'keySplines', 'from', 'to', 'by'], | ||
'conditionalProcessing': ['requiredFeatures', 'requiredExtensions', 'systemLanguage'], | ||
'core': ['id', 'xml:base', 'xml:lang', 'xml:space'], | ||
'graphicalEvent': ['onfocusin', 'onfocusout', 'onactivate', 'onclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onload'], | ||
'presentation': ['alignment-baseline', 'baseline-shift', 'clip', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'display', 'dominant-baseline', 'enable-background', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'mask', 'opacity', 'overflow', 'pointer-events', 'shape-rendering', 'stop-color', 'stop-opacity', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'unicode-bidi', 'visibility', 'word-spacing', 'writing-mode'], | ||
'xlink': ['xlink:href', 'xlink:show', 'xlink:actuate', 'xlink:type', 'xlink:role', 'xlink:arcrole', 'xlink:title'], | ||
'documentEvent': ['onunload', 'onabort', 'onerror', 'onresize', 'onscroll', 'onzoom'] | ||
}; | ||
// http://www.w3.org/TR/SVG/single-page.html#types-ColorKeywords | ||
@@ -110,0 +925,0 @@ exports.colorsNames = { |
@@ -1,2 +0,2 @@ | ||
var flattenOneLevel = require('../lib/tools').flattenOneLevel; | ||
var flattenOneLevel = require('../lib/svgo/tools').flattenOneLevel; | ||
@@ -3,0 +3,0 @@ /* |
@@ -1,2 +0,2 @@ | ||
var cleanupOutData = require('../lib/tools').cleanupOutData, | ||
var cleanupOutData = require('../lib/svgo/tools').cleanupOutData, | ||
regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/, | ||
@@ -29,3 +29,3 @@ regPathData = /(?=-)|[\s,]+/, | ||
if (data.length) { | ||
data = convertToRelative(data, params); | ||
data = convertToRelative(data); | ||
@@ -78,3 +78,3 @@ data = filters(data, params); | ||
// very stupid defense strategy | ||
if (!isNaN(data[0])) { | ||
if (typeof data[0] === 'number' && !isNaN(data[0])) { | ||
@@ -120,3 +120,3 @@ var pair = 0; | ||
*/ | ||
function convertToRelative(path, params) { | ||
function convertToRelative(path) { | ||
@@ -133,2 +133,3 @@ var instruction, | ||
// data !== !z | ||
if (data) { | ||
@@ -156,107 +157,84 @@ | ||
// convert absolute path data coordinates to relative | ||
if (params.convertToRelative) { | ||
// M → m | ||
// L → l | ||
// T → t | ||
if ('MLT'.indexOf(instruction) > -1) { | ||
// M → m | ||
// L → l | ||
// T → t | ||
if ('MLT'.indexOf(instruction) > -1) { | ||
// don't convert M if point is [0, 0] | ||
if (instruction !== 'M' || point[0] !== 0 && point[1] !== 0) { | ||
instruction = instruction.toLowerCase(); | ||
} | ||
// x y | ||
// 0 1 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
// x y | ||
// 0 1 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
point[0] += data[0]; | ||
point[1] += data[1]; | ||
point[0] += data[0]; | ||
point[1] += data[1]; | ||
// C → c | ||
} else if (instruction === 'C') { | ||
// C → c | ||
} else if (instruction === 'C') { | ||
instruction = 'c'; | ||
instruction = 'c'; | ||
// x1 y1 x2 y2 x y | ||
// 0 1 2 3 4 5 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
data[4] -= point[0]; | ||
data[5] -= point[1]; | ||
// x1 y1 x2 y2 x y | ||
// 0 1 2 3 4 5 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
data[4] -= point[0]; | ||
data[5] -= point[1]; | ||
point[0] += data[4]; | ||
point[1] += data[5]; | ||
point[0] += data[4]; | ||
point[1] += data[5]; | ||
// S → s | ||
// Q → q | ||
} else if ('SQ'.indexOf(instruction) > -1) { | ||
// S → s | ||
// Q → q | ||
} else if ('SQ'.indexOf(instruction) > -1) { | ||
instruction = instruction.toLowerCase(); | ||
instruction = instruction.toLowerCase(); | ||
// x1 y1 x y | ||
// 0 1 2 3 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
// x1 y1 x y | ||
// 0 1 2 3 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[2] -= point[0]; | ||
data[3] -= point[1]; | ||
point[0] += data[2]; | ||
point[1] += data[3]; | ||
point[0] += data[2]; | ||
point[1] += data[3]; | ||
// A → a | ||
} else if (instruction === 'A') { | ||
// A → a | ||
} else if (instruction === 'A') { | ||
instruction = 'a'; | ||
instruction = 'a'; | ||
// rx ry x-axis-rotation large-arc-flag sweep-flag x y | ||
// 0 1 2 3 4 5 6 | ||
data[0] -= point[0]; | ||
data[1] -= point[1]; | ||
data[5] -= point[0]; | ||
data[6] -= point[1]; | ||
// rx ry x-axis-rotation large-arc-flag sweep-flag x y | ||
// 0 1 2 3 4 5 6 | ||
data[5] -= point[0]; | ||
data[6] -= point[1]; | ||
point[0] += data[5]; | ||
point[1] += data[6]; | ||
point[0] += data[5]; | ||
point[1] += data[6]; | ||
// H → h | ||
} else if (instruction === 'H') { | ||
// H → h | ||
} else if (instruction === 'H') { | ||
instruction = 'h'; | ||
instruction = 'h'; | ||
data[0] -= point[0]; | ||
data[0] -= point[0]; | ||
point[0] += data[0]; | ||
point[0] += data[0]; | ||
// V → v | ||
} else if (instruction === 'V') { | ||
// V → v | ||
} else if (instruction === 'V') { | ||
instruction = 'v'; | ||
instruction = 'v'; | ||
data[0] -= point[1]; | ||
data[0] -= point[1]; | ||
point[1] += data[0]; | ||
point[1] += data[0]; | ||
} | ||
// calculate new current point | ||
} else { | ||
if ('MCSLQTA'.indexOf(instruction) > -1) { | ||
newPoint = data.slice(-2); | ||
point[0] = newPoint[0]; | ||
point[1] = newPoint[1]; | ||
} else if (instruction === 'H') { | ||
point[0] = data[0]; | ||
} else if (instruction === 'V') { | ||
point[1] = data[0]; | ||
} | ||
} | ||
@@ -270,2 +248,7 @@ | ||
// !data === z, reset current point | ||
else { | ||
point = [0, 0]; | ||
} | ||
}); | ||
@@ -292,4 +275,3 @@ | ||
point: [0, 0] | ||
}, | ||
index = 0; | ||
}; | ||
@@ -302,4 +284,2 @@ path = path.filter(function(item) { | ||
index++; | ||
if (data) { | ||
@@ -311,2 +291,89 @@ | ||
// convert straight curves into lines segments | ||
if (params.straightCurves) { | ||
// c | ||
if ( | ||
instruction === 'c' && | ||
isCurveStraightLine( | ||
[ 0, data[0], data[2], data[4] ], | ||
[ 0, data[1], data[3], data[5] ] | ||
) | ||
) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
// s | ||
else if (instruction === 's') { | ||
if ( | ||
isCurveStraightLine( | ||
[ 0, data[0], data[2] ], | ||
[ 0, data[1], data[3] ] | ||
) | ||
) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
} | ||
// q | ||
else if ( | ||
prev.item && | ||
instruction === 'q' && | ||
isCurveStraightLine( | ||
[ 0, data[0], data[2] ], | ||
[ 0, data[1], data[3] ] | ||
) | ||
) { | ||
// save the original one for the future potential q + t conversion | ||
item.original = { | ||
instruction: instruction, | ||
data: data | ||
}; | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
else if (instruction === 't') { | ||
// q (original) + t | ||
if ( | ||
prev.item && | ||
prev.item.original && | ||
prev.item.original.instruction === 'q' | ||
) { | ||
if (isCurveStraightLine( | ||
[ prev.item.original.data[0], prev.item.original.data[2], data[0] ], | ||
[ prev.item.original.data[1], prev.item.original.data[3], data[1] ] | ||
)) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} else { | ||
prev.item.instruction = 'q'; | ||
prev.item.data = prev.item.original.data; | ||
} | ||
} | ||
// [^qt] + t | ||
else if (!prev.item || 'qt'.indexOf(prev.item.instruction) === -1) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
} | ||
// a | ||
else if ( | ||
instruction === 'a' && | ||
(data[0] === 0 || data[1] === 0) | ||
) { | ||
instruction = 'l'; | ||
data = data.slice(-2); | ||
} | ||
} | ||
// horizontal and vertical line shorthands | ||
@@ -317,11 +384,9 @@ // l 50 0 → h 50 | ||
params.lineShorthands && | ||
'Ll'.indexOf(instruction) > -1 | ||
instruction === 'l' | ||
) { | ||
var lowerCase = instruction === instruction.toLowerCase(); | ||
if (point[1] - prev.point[1] === 0) { | ||
instruction = lowerCase ? 'h' : 'H'; | ||
if (data[1] === 0) { | ||
instruction = 'h'; | ||
data = [data[0]]; | ||
} else if (point[0] - prev.point[0] === 0) { | ||
instruction = lowerCase ? 'v' : 'V'; | ||
} else if (data[0] === 0) { | ||
instruction = 'v'; | ||
data = [data[1]]; | ||
@@ -331,2 +396,67 @@ } | ||
// convert curves into smooth shorthands | ||
if (params.curveSmoothShorthands && prev.item) { | ||
// curveto | ||
if (instruction === 'c') { | ||
// c + c → c + s | ||
if ( | ||
prev.item.instruction === 'c' && | ||
data[0] === -(prev.item.data[2] - prev.item.data[4]) && | ||
data[1] === -(prev.item.data[3] - prev.item.data[5]) | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
// s + c → s + s | ||
else if ( | ||
prev.item.instruction === 's' && | ||
data[0] === -(prev.item.data[0] - prev.item.data[2]) && | ||
data[1] === -(prev.item.data[1] - prev.item.data[3]) | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
// [^cs] + c → [^cs] + s | ||
else if ( | ||
'cs'.indexOf(prev.item.instruction) === -1 && | ||
data[0] === 0 && | ||
data[1] === 0 | ||
) { | ||
instruction = 's'; | ||
data = data.slice(2); | ||
} | ||
} | ||
// quadratic Bézier curveto | ||
else if (instruction === 'q') { | ||
// q + q → q + t | ||
if ( | ||
prev.item.instruction === 'q' && | ||
data[0] === (prev.item.data[2] - prev.item.data[0]) && | ||
data[1] === (prev.item.data[3] - prev.item.data[1]) | ||
) { | ||
instruction = 't'; | ||
data = data.slice(2); | ||
} | ||
// t + q → t + t | ||
else if ( | ||
prev.item.instruction === 't' && | ||
data[2] === prev.item.data[0] && | ||
data[3] === prev.item.data[1] | ||
) { | ||
instruction = 't'; | ||
data = data.slice(2); | ||
} | ||
} | ||
} | ||
// remove useless non-first path segments | ||
@@ -338,4 +468,3 @@ if (params.removeUseless) { | ||
( | ||
'lhvqtcs'.indexOf(instruction) > -1 || | ||
(instruction === 'm' && index > 1) | ||
'mlhvqtcs'.indexOf(instruction) > -1 | ||
) && | ||
@@ -347,22 +476,7 @@ data.every(function(i) { return i === 0; }) | ||
// M25,25 L25,25 C 25,25 25,25 25,25 | ||
if ( | ||
'LHVQTCS'.indexOf(instruction) > -1 || | ||
(instruction === 'M' && index > 1) | ||
) { | ||
var i = -1, | ||
every = data.every(function(d) { | ||
return d - prev.point[++i % 2] === 0; | ||
}); | ||
if (every) { | ||
return false; | ||
} | ||
} | ||
// a 25,25 -30 0,1 0,0 | ||
if ( | ||
'aA'.indexOf(item.instruction) > -1 && | ||
point[0] - prev.point[0] === 0 && | ||
point[1] - prev.point[1] === 0 | ||
instruction === 'a' && | ||
data[5] === 0 && | ||
data[6] === 0 | ||
) { | ||
@@ -383,5 +497,2 @@ return false; | ||
prev.item.data[0] += data[0]; | ||
// replace previous H or V data with current | ||
} else if (instruction === 'H' || instruction === 'V') { | ||
prev.item.data[0] = data[0]; | ||
// concat previous data with current | ||
@@ -392,2 +503,5 @@ } else { | ||
// if there was an original then remove it because of the new data | ||
delete prev.item.original; | ||
// filter current item | ||
@@ -433,2 +547,28 @@ return false; | ||
/** | ||
* Checks if curve is a straight line by calculating a polygon area. | ||
* | ||
* @see http://www.mathopenref.com/coordpolygonarea2.html | ||
* | ||
* @param {Array} xs array of curve points x-coordinates | ||
* @param {Array} ys array of curve points y-coordinates | ||
* | ||
* @return {Boolean} | ||
*/ | ||
function isCurveStraightLine(xs, ys) { | ||
var points = xs.length, | ||
area = 0, | ||
j = points - 1; | ||
for (var i=0; i < points; i++) { | ||
area += (xs[j] + xs[i]) * (ys[j] - ys[i]); | ||
j = i; | ||
} | ||
return !+area.toFixed(2); | ||
} | ||
/** | ||
* Convert path array to string. | ||
@@ -435,0 +575,0 @@ * |
@@ -1,2 +0,2 @@ | ||
var extend = require('../lib/tools').extend, | ||
var extend = require('../lib/svgo/tools').extend, | ||
stylingProps = require('./_collections').stylingProps, | ||
@@ -3,0 +3,0 @@ regCleanupStyle = /(:|;)\s+/g; |
@@ -1,2 +0,2 @@ | ||
var cleanupOutData = require('../lib/tools').cleanupOutData, | ||
var cleanupOutData = require('../lib/svgo/tools').cleanupOutData, | ||
regTransformTypes = /matrix|translate|scale|rotate|skewX|skewY/, | ||
@@ -3,0 +3,0 @@ regTransformSplit = /(matrix|translate|scale|rotate|skewX|skewY)\s*\((.+?)\)[\s,]*/, |
@@ -1,5 +0,6 @@ | ||
var intersectAttrs = require('../lib/tools').intersectAttrs; | ||
var nonInheritedAttrs = require('./_collections').nonInheritedAttrs; | ||
/** | ||
* Collapse content's intersected attributes to the existing group wrapper. | ||
* Collapse content's intersected and inheritable | ||
* attributes to the existing group wrapper. | ||
* | ||
@@ -38,3 +39,3 @@ * @example | ||
} else { | ||
intersection = intersectAttrs(intersection, g.attrs); | ||
intersection = intersectInheritableAttrs(intersection, g.attrs); | ||
@@ -48,5 +49,6 @@ if (!intersection) return false; | ||
if (every) { | ||
if (every) { | ||
item.content.forEach(function(g) { | ||
for (var name in intersection) { | ||
@@ -64,3 +66,5 @@ g.removeAttr(name); | ||
} | ||
}); | ||
} | ||
@@ -71,1 +75,32 @@ | ||
}; | ||
/** | ||
* Intersect inheritable attributes. | ||
* | ||
* @param {Object} a first attrs object | ||
* @param {Object} b second attrs object | ||
* | ||
* @return {Object} intersected attrs object | ||
*/ | ||
function intersectInheritableAttrs(a, b) { | ||
var c = {}; | ||
for (var n in a) { | ||
if ( | ||
b.hasOwnProperty(n) && | ||
nonInheritedAttrs.indexOf(n) === -1 && | ||
a[n].name === b[n].name && | ||
a[n].value === b[n].value && | ||
a[n].prefix === b[n].prefix && | ||
a[n].local === b[n].local | ||
) { | ||
c[n] = a[n]; | ||
} | ||
} | ||
if (!Object.keys(c).length) return false; | ||
return c; | ||
} |
@@ -16,4 +16,6 @@ /** | ||
return !item.comment; | ||
if (item.comment && item.comment.charAt(0) !== '!') { | ||
return false; | ||
} | ||
}; |
@@ -1,2 +0,2 @@ | ||
var container = require('./_collections').elems.container; | ||
var container = require('./_collections').elemsGroups.container; | ||
@@ -3,0 +3,0 @@ /** |
@@ -1,10 +0,4 @@ | ||
``` | ||
o-o o o o--o o-o | ||
\ \ / | | | | | ||
o-o o o--o o-o | ||
| | ||
o--o | ||
``` | ||
![logo](https://raw.github.com/svg/svgo/master/logo200x200.png) | ||
## SVGO v0.0.9 [![Build Status](https://secure.travis-ci.org/svg/svgo.png)](http://travis-ci.org/svg/svgo) | ||
## SVGO v0.1.0 [![Build Status](https://secure.travis-ci.org/svg/svgo.png)](http://travis-ci.org/svg/svgo) | ||
@@ -36,7 +30,7 @@ **SVG** **O**ptimizer is a Nodejs-based tool for optimizing SVG vector graphics files. | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/cleanupEnableBackground.js) ] remove or cleanup enable-background attribute | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/cleanupSVGElem.js) ] cleanup SVG element from useless attributes | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertStyleToAttrs.js) ] convert styles into attributes | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertColors.js) ] convert colors (from rgb() to #rrggbb, from #rrggbb to #rgb) | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js) ] convert Path data to relative, trim useless delimiters and much more | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertPathData.js) ] convert Path data to relative, convert one segment to another, trim useless delimiters and much more | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/convertTransform.js) ] collapse multiple transforms into one, convert matrices to the short aliases and much more | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeUnknownsAndDefaults.js) ] remove unknown elements content and attributes, remove attrs with default values | ||
* [ [>](https://github.com/svg/svgo/blob/master/plugins/removeUnusedNS.js) ] remove unused namespaces declaration | ||
@@ -51,7 +45,7 @@ * [ [>](https://github.com/svg/svgo/blob/master/plugins/moveElemsAttrsToGroup.js) ] move elements attributes to the existing group wrapper | ||
```sh | ||
$ [sudo] npm install -g svgo | ||
``` | ||
npm install -g svgo | ||
``` | ||
``` | ||
```sh | ||
Usage: | ||
@@ -63,4 +57,6 @@ svgo [OPTIONS] [ARGS] | ||
-v, --version : Version | ||
-i INPUT, --input=INPUT : Input: stdin (default) | filename | Data URI base64 string | ||
-o OUTPUT, --output=OUTPUT : Output: stdout (default) | filename | ||
-i INPUT, --input=INPUT : Input file, "-" for STDIN | ||
-s STRING, --string=STRING : Input SVG data string | ||
-f FOLDER, --folder=FOLDER : Input folder, optimize and rewrite all *.svg files | ||
-o OUTPUT, --output=OUTPUT : Output file (by default the same as the input), "-" for STDOUT | ||
-c CONFIG, --config=CONFIG : Local config file to extend default | ||
@@ -71,30 +67,50 @@ --disable=DISABLE : Disable plugin by name | ||
--pretty : Make SVG pretty printed | ||
--test : Make a visual comparison of two files (PhantomJS pre-required) | ||
Arguments: | ||
INPUT : Alias to --input | ||
OUTPUT : Alias to --output | ||
``` | ||
With files: | ||
* with files: | ||
```sh | ||
$ svgo test.svg | ||
``` | ||
svgo -i test.svg -o test.min.svg | ||
```sh | ||
$ svgo test.svg test.min.svg | ||
``` | ||
With stdin / stdout: | ||
* with STDIN / STDOUT: | ||
```sh | ||
$ cat test.svg | svgo -i - -o - > test.min.svg | ||
``` | ||
cat test.svg | svgo > test.min.svg | ||
* with folder | ||
```sh | ||
$ svgo -f ../path/to/folder/with/svg/files | ||
``` | ||
With Data URI base64 strings: | ||
* with strings (even with Data URI base64): | ||
```sh | ||
$ svgo -s '<svg version="1.1">test</svg>' -o test.min.svg | ||
``` | ||
svgo -i 'data:image/svg+xml;base64,…' -o test.min.svg | ||
```sh | ||
$ svgo -s 'data:image/svg+xml;base64,…' -o test.min.svg | ||
``` | ||
* with [GUI](https://github.com/svg/svgo-gui) (currently Mac OS X app only) | ||
* as a Nodejs module - [examples](https://github.com/svg/svgo/tree/master/examples) | ||
## TODO | ||
1. batch folder optimization | ||
2. more plugins | ||
3. SVGO GUI (crossplatform?) via awesome [node-webkit](https://github.com/rogerwang/node-webkit) | ||
4. online SVGO web service | ||
5. more unit tests | ||
6. … | ||
* [v0.1.1](https://github.com/svg/svgo/issues?milestone=3&state=open) | ||
* [v0.1.2](https://github.com/svg/svgo/issues?milestone=4&state=open) | ||
## License and copyrights | ||
This software is released under the terms of the [MIT license](https://github.com/svg/svgo/blob/master/LICENSE). | ||
Logo by [Yegor Bolshakov](http://xizzzy.ru/). |
@@ -1,2 +0,3 @@ | ||
var config = require('../../lib/config'); | ||
var cover = process.argv[3] === 'mocha-istanbul', | ||
config = require(cover ? '../../lib-cov/svgo/config' : '../../lib/svgo/config'); | ||
@@ -30,3 +31,3 @@ function getPlugin(name, config) { | ||
}) | ||
.end(); | ||
.done(); | ||
}); | ||
@@ -71,3 +72,3 @@ | ||
}) | ||
.end(); | ||
.done(); | ||
}); | ||
@@ -85,3 +86,3 @@ | ||
coa: { | ||
config: './test/config/config.json' | ||
config: './test/config/config.yml' | ||
} | ||
@@ -96,3 +97,3 @@ }, | ||
}) | ||
.end(); | ||
.done(); | ||
}); | ||
@@ -99,0 +100,0 @@ |
@@ -52,3 +52,3 @@ var INHERIT = require('inherit'), | ||
}) | ||
.end(); | ||
.done(); | ||
}); | ||
@@ -70,3 +70,3 @@ | ||
return mySVGO.optimize(input.toString()); | ||
return mySVGO.fromString(input.toString()); | ||
}) | ||
@@ -73,0 +73,0 @@ .then(function(min) { |
@@ -1,2 +0,3 @@ | ||
var svg2js = require('../../lib/svg2js'); | ||
var cover = process.argv[3] === 'mocha-istanbul', | ||
svg2js = require(cover ? '../../lib-cov/svgo/svg2js' : '../../lib/svgo/svg2js'); | ||
@@ -3,0 +4,0 @@ describe('svg2js', function() { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Wildcard dependency
QualityPackage has a dependency with a floating version range. This can cause issues if the dependency publishes a new major version.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
225591
185
4187
2
112
0
7
4
1
80
8
+ Addedyamljs@~0.1.3
+ Addedargparse@0.1.16(transitive)
+ Addedglob@3.1.21(transitive)
+ Addedgraceful-fs@1.2.3(transitive)
+ Addedinherits@1.0.2(transitive)
+ Addedlru-cache@2.7.3(transitive)
+ Addedminimatch@0.2.14(transitive)
+ Addedsigmund@1.0.1(transitive)
+ Addedunderscore@1.7.0(transitive)
+ Addedunderscore.string@2.4.0(transitive)
+ Addedyamljs@0.1.6(transitive)
Updatedcoa@~0.3.7
Updatedq@~0.8.10
Updatedq-fs@~0.1
Updatedsax@~0.4