1
0
mirror of https://github.com/svg/svgo.git synced 2025-07-29 20:21:14 +03:00

Path data processing improvements.

Improved path data saving.
Collapse repeated now works at writing path data and doesn't affect pathJS to make further processing easier.
Resolves #204.
This commit is contained in:
GreLI
2015-03-16 23:43:38 +03:00
parent 15ded2773e
commit e220b6cad0
9 changed files with 248 additions and 272 deletions

View File

@ -1,8 +1,8 @@
'use strict'; 'use strict';
var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/, var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
regPathData = /[\-+]?\d*\.?\d+([eE][\-+]?\d+)?/g, regPathData = /[-+]?(?:\d*\.\d+|\d+\.?)([eE][-+]?\d+)?/g,
regNumericValues = /[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?/, regNumericValues = /[-+]?(\d*\.\d+|\d+\.?)(?:[eE][-+]?\d+)?/,
transform2js = require('./_transforms').transform2js, transform2js = require('./_transforms').transform2js,
transformsMultiply = require('./_transforms').transformsMultiply, transformsMultiply = require('./_transforms').transformsMultiply,
collections = require('./_collections.js'), collections = require('./_collections.js'),
@ -18,85 +18,69 @@ var regPathInstructions = /([MmLlHhVvCcSsQqTtAaZz])\s*/,
* @param {Object} params plugin params * @param {Object} params plugin params
* @return {Array} output array * @return {Array} output array
*/ */
exports.path2js = function(pathString) { exports.path2js = function(path) {
if (path.pathJS) return path.pathJS;
// JS representation of the path data var paramsLength = { // Number of parameters of every path command
var path = [], H: 1, V: 1, M: 2, L: 2, T: 2, Q: 4, S: 4, C: 6, A: 7,
// current instruction context h: 1, v: 1, m: 2, l: 2, t: 2, q: 4, s: 4, c: 6, a: 7
instruction; },
pathData = [], // JS representation of the path data
instruction, // current instruction context
startMoveto = false;
// splitting path string into array like ['M', '10 50', 'L', '20 30'] // splitting path string into array like ['M', '10 50', 'L', '20 30']
pathString.split(regPathInstructions).forEach(function(data) { path.attr('d').value.split(regPathInstructions).forEach(function(data) {
if (data) { if (!data) return;
// instruction item if (!startMoveto) {
if (regPathInstructions.test(data)) { if (data == 'M' || data == 'm') {
instruction = data; startMoveto = true;
} else return;
}
// z - instruction w/o data // instruction item
if ('Zz'.indexOf(instruction) > -1) { if (regPathInstructions.test(data)) {
path.push({ instruction = data;
instruction: 'z'
});
}
// data item
} else {
data = data.trim().match(regPathData); // z - instruction w/o data
if (instruction == 'Z' || instruction == 'z') {
pathData.push({
instruction: 'z'
});
}
// data item
} else {
data = data.match(regPathData);
if (!data) return;
if (data) { data = data.map(Number);
var index = 0, // Subsequent moveto pairs of coordinates are threated as implicit lineto commands
pair = 2; // http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
if (instruction == 'M' || instruction == 'm') {
data = data.map(function(str) { pathData.push({
return +str; instruction: pathData.length == 0 ? 'M' : instruction,
}); data: data.splice(0, 2)
});
// deal with very first 'Mm' and multiple points data instruction = instruction == 'M' ? 'L' : 'l';
if ('Mm'.indexOf(instruction) > -1) { }
path.push({
instruction: instruction,
data: data.slice(index, index + pair)
});
index += pair;
if (data.length) {
instruction = instruction === instruction.toLowerCase() ? 'l' : 'L';
}
}
if ('HhVv'.indexOf(instruction) > -1) {
pair = 1;
} else if ('LlTt'.indexOf(instruction) > -1) {
pair = 2;
} else if ('QqSs'.indexOf(instruction) > -1) {
pair = 4;
} else if ('Cc'.indexOf(instruction) > -1) {
pair = 6;
} else if ('Aa'.indexOf(instruction) > -1) {
pair = 7;
}
while(index < data.length) {
path.push({
instruction: instruction,
data: data.slice(index, index + pair)
});
index += pair;
}
}
for (var pair = paramsLength[instruction]; data.length;) {
pathData.push({
instruction: instruction,
data: data.splice(0, pair)
});
} }
} }
}); });
return path; // First moveto is actually absolute. Subsequent coordinates were separated above.
if (pathData.length && pathData[0].instruction == 'm') {
pathData[0].instruction = 'M';
}
path.pathJS = pathData;
return pathData;
}; };
/** /**
@ -105,79 +89,76 @@ exports.path2js = function(pathString) {
* @param {Array} data input data * @param {Array} data input data
* @return {Array} output data * @return {Array} output data
*/ */
exports.relative2absolute = function(data) { var relative2absolute = exports.relative2absolute = function(data) {
var currentPoint = [0, 0], var currentPoint = [0, 0],
subpathPoint = [0, 0], subpathPoint = [0, 0],
i; i;
data.forEach(function(item) { data = data.map(function(item) {
if (item.instruction === 'M') { var instruction = item.instruction,
itemData = item.data && item.data.slice();
currentPoint = item.data.slice(-2); if (instruction == 'M') {
subpathPoint = item.data.slice(-2);
} else if ('mlcsqta'.indexOf(item.instruction) > -1) { set(currentPoint, itemData);
set(subpathPoint, itemData);
for (i = 0; i < item.data.length; i++) { } else if ('mlcsqt'.indexOf(instruction) > -1) {
if (i % 2 === 0) {
item.data[i] += currentPoint[0];
} else {
item.data[i] += currentPoint[1];
}
if (i > 0) { for (i = 0; i < itemData.length; i++) {
var index = i + 1; itemData[i] += currentPoint[i % 2];
}
set(currentPoint, itemData);
if ('mlt'.indexOf(item.instruction) > -1 && index % 2 === 0) { if (instruction == 'm') {
currentPoint[0] = item.data[i - 1]; set(subpathPoint, itemData);
currentPoint[1] = item.data[i];
} else if ('qs'.indexOf(item.instruction) > -1 && index % 4 === 0) {
currentPoint[0] = item.data[i - 1];
currentPoint[1] = item.data[i];
} else if (item.instruction === 'c' && index % 6 === 0) {
currentPoint[0] = item.data[i - 1];
currentPoint[1] = item.data[i];
} else if (item.instruction === 'a' && index % 7 === 0) {
currentPoint[0] = item.data[i - 1];
currentPoint[1] = item.data[i];
}
}
} }
if (item.instruction === 'm') { } else if (instruction == 'a') {
subpathPoint = item.data.slice(-2);
}
} else if (item.instruction === 'h') { itemData[5] += currentPoint[0];
itemData[6] += currentPoint[1];
set(currentPoint, itemData);
for (i = 0; i < item.data.length; i++) { } else if (instruction == 'h') {
item.data[i] += currentPoint[0];
}
currentPoint[0] = item.data[item.data.length - 1]; itemData[0] += currentPoint[0];
currentPoint[0] = itemData[0];
} else if (item.instruction === 'v') { } else if (instruction == 'v') {
for (i = 0; i < item.data.length; i++) { itemData[0] += currentPoint[1];
item.data[i] += currentPoint[1]; currentPoint[1] = itemData[0];
}
currentPoint[1] = item.data[item.data.length - 1]; } else if ('MZLCSQTA'.indexOf(instruction) > -1) {
} else { set(currentPoint, itemData);
currentPoint = subpathPoint; } else if (instruction == 'H') {
currentPoint[0] = itemData[0];
} else if (instruction == 'V') {
currentPoint[1] = itemData[0];
} else if (instruction == 'z') {
set(currentPoint, subpathPoint);
} }
item.instruction = item.instruction.toUpperCase(); return instruction == 'z' ?
{ instruction: 'z' } :
{
instruction: instruction.toUpperCase(),
data: itemData
};
}); });
return data; return data;
}; };
/** /**
@ -241,27 +222,26 @@ exports.applyTransforms = function(elem, path, applyTransformsStroked, floatPrec
// If an 'a' command can't be transformed directly, convert path to curves. // If an 'a' command can't be transformed directly, convert path to curves.
if (!splittedMatrix.isSimple && path.some(function(i) { return i.instruction == 'a' })) { if (!splittedMatrix.isSimple && path.some(function(i) { return i.instruction == 'a' })) {
var prev; var prev;
path = path.reduce(function(newPath, item){ path.forEach(function(item, index, path){
if (item.instruction == 'a') { if (item.instruction == 'a') {
var curves = a2c.apply(0, [0, 0].concat(item.data)), var curves = a2c.apply(0, [0, 0].concat(item.data)),
items = [],
curveData; curveData;
while ((curveData = curves.splice(0,6)).length) { while ((curveData = curves.splice(0,6)).length) {
item = { var base = prev.coords;
items.push(prev = {
instruction: 'c', instruction: 'c',
data: curveData, data: curveData,
coords: [base[0] + item.data[4], base[1] + item.data[5]],
base: prev.coords base: prev.coords
}; });
item.coords = [item.base[0] + item.data[4], item.base[1] + item.data[5]];
prev = item;
newPath.push(item);
} }
path.splice.apply(path, [index, 1].concat(items));
} else { } else {
newPath.push(item);
if (prev) item.base = prev.coords; if (prev) item.base = prev.coords;
prev = item; prev = item;
} }
return newPath; });
}, []);
} }
path.forEach(function(pathItem) { path.forEach(function(pathItem) {
@ -291,8 +271,8 @@ exports.applyTransforms = function(elem, path, applyTransformsStroked, floatPrec
// then apply it only to the first absoluted M // then apply it only to the first absoluted M
newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]); newPoint = transformPoint(matrix.data, pathItem.data[0], pathItem.data[1]);
pathItem.data[0] = pathItem.coords[0] = newPoint[0]; set(pathItem.data, newPoint);
pathItem.data[1] = pathItem.coords[1] = newPoint[1]; set(pathItem.coords, newPoint);
// clear translate() data from transform matrix // clear translate() data from transform matrix
matrix.data[4] = 0; matrix.data[4] = 0;
@ -553,21 +533,63 @@ function computeQuadraticFirstDerivativeRoot(a, b, c) {
* @param {Object} params plugin params * @param {Object} params plugin params
* @return {String} output path string * @return {String} output path string
*/ */
exports.js2path = function(path, params) { exports.js2path = function(path, data, params) {
// output path data string path.pathJS = data;
var pathString = '';
path.forEach(function(item) { if (params.collapseRepeated) {
data = collapseRepeated(data);
}
pathString += item.instruction + (item.data ? cleanupOutData(item.data, params) : ''); path.attr('d').value = data.reduce(function(pathString, item) {
return pathString += item.instruction + (item.data ? cleanupOutData(item.data, params) : '');
}); }, '');
return pathString;
}; };
/**
* Collapse repeated instructions data
*
* @param {Array} path input path data
* @return {Array} output path data
*/
function collapseRepeated(data) {
var prev,
prevIndex;
// copy an array and modifieds item to keep original data untouched
data = data.reduce(function(newPath, item) {
if (
prev && item.data &&
item.instruction == prev.instruction
) {
// concat previous data with current
prev = newPath[prevIndex] = {
instruction: prev.instruction,
data: prev.data.concat(item.data),
coords: item.coords,
base: prev.base
}
} else {
newPath.push(item);
prev = item;
prevIndex = newPath.length - 1;
}
return newPath;
}, []);
return data;
}
function set(dest, source) {
dest[0] = source[source.length - 2];
dest[1] = source[source.length - 1];
return dest;
}
/* Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/ /* Based on code from Snap.svg (Apache 2 license). http://snapsvg.io/
* Thanks to Dmitry Baranovskiy for his great work! * Thanks to Dmitry Baranovskiy for his great work!
*/ */

View File

@ -51,7 +51,7 @@ exports.fn = function(item, params) {
error = precision !== false ? +Math.pow(.1, precision).toFixed(precision) : 1e-2; error = precision !== false ? +Math.pow(.1, precision).toFixed(precision) : 1e-2;
hasMarkerMid = item.hasAttr('marker-mid'); hasMarkerMid = item.hasAttr('marker-mid');
var data = path2js(item.attr('d').value); var data = path2js(item);
// TODO: get rid of functions returns // TODO: get rid of functions returns
if (data.length) { if (data.length) {
@ -63,17 +63,11 @@ exports.fn = function(item, params) {
data = filters(data, params); data = filters(data, params);
if (params.collapseRepeated) {
data = collapseRepeated(data, params);
}
if (params.utilizeAbsolute) { if (params.utilizeAbsolute) {
data = convertToMixed(data, params); data = convertToMixed(data, params);
} }
item.pathJS = data; js2path(item, data, params);
item.attr('d').value = js2path(data, params);
} }
} }
@ -91,7 +85,6 @@ function convertToRelative(path) {
var point = [0, 0], var point = [0, 0],
subpathPoint = [0, 0], subpathPoint = [0, 0],
mM = false,
baseItem; baseItem;
path.forEach(function(item, index) { path.forEach(function(item, index) {
@ -106,18 +99,12 @@ function convertToRelative(path) {
// recalculate current point // recalculate current point
if ('mcslqta'.indexOf(instruction) > -1) { if ('mcslqta'.indexOf(instruction) > -1) {
var newPoint = data.slice(-2); point[0] += data[data.length - 2];
point[1] += data[data.length - 1];
point[0] += newPoint[0];
point[1] += newPoint[1];
if (instruction === 'm') { if (instruction === 'm') {
if (index === 0) { subpathPoint[0] = point[0];
instruction = 'M'; subpathPoint[1] = point[1];
mM = true;
}
subpathPoint = point.slice(-2);
baseItem = item; baseItem = item;
} }
@ -134,20 +121,16 @@ function convertToRelative(path) {
// convert absolute path data coordinates to relative // convert absolute path data coordinates to relative
// if "M" was not transformed from "m" // if "M" was not transformed from "m"
// M → m // M → m
if ( if (instruction === 'M') {
instruction === 'M' &&
(!mM || index > 0)
) {
if (index > 0) instruction = 'm'; if (index > 0) instruction = 'm';
data[0] -= point[0]; data[0] -= point[0];
data[1] -= point[1]; data[1] -= point[1];
point[0] += data[0]; subpathPoint[0] = point[0] += data[0];
point[1] += data[1]; subpathPoint[1] = point[1] += data[1];
subpathPoint = point.slice(-2);
baseItem = item; baseItem = item;
} }
@ -241,11 +224,12 @@ function convertToRelative(path) {
} }
// !data === z, reset current point // !data === z, reset current point
else { else if (instruction == 'z') {
if(baseItem) if (baseItem) {
item.coords = baseItem.coords; item.coords = baseItem.coords;
point = subpathPoint; }
mM = false; point[0] = subpathPoint[0];
point[1] = subpathPoint[1];
} }
item.base = index > 0 ? path[index - 1].coords : [0, 0]; item.base = index > 0 ? path[index - 1].coords : [0, 0];
@ -267,7 +251,7 @@ function filters(path, params) {
var relSubpoint = [0, 0], var relSubpoint = [0, 0],
pathBase = [0, 0], pathBase = [0, 0],
prev; prev = {};
path = path.filter(function(item, index) { path = path.filter(function(item, index) {
@ -279,7 +263,7 @@ function filters(path, params) {
if (instruction === 's') { if (instruction === 's') {
var sdata = [0, 0].concat(data); var sdata = [0, 0].concat(data);
if (prev && 'cs'.indexOf(prev.instruction) > -1) { if ('cs'.indexOf(prev.instruction) > -1) {
var pdata = prev.data, var pdata = prev.data,
n = pdata.length; n = pdata.length;
@ -351,7 +335,6 @@ function filters(path, params) {
// q // q
else if ( else if (
prev &&
instruction === 'q' && instruction === 'q' &&
isCurveStraightLine( isCurveStraightLine(
[ 0, data[0], data[2] ], [ 0, data[0], data[2] ],
@ -372,7 +355,6 @@ function filters(path, params) {
// q (original) + t // q (original) + t
if ( if (
prev &&
prev.original && prev.original &&
prev.original.instruction === 'q' prev.original.instruction === 'q'
) { ) {
@ -389,7 +371,7 @@ function filters(path, params) {
} }
// [^qt] + t // [^qt] + t
else if (!prev || 'qt'.indexOf(prev.instruction) === -1) { else if ('qt'.indexOf(prev.instruction) < 0) {
instruction = 'l'; instruction = 'l';
data = data.slice(-2); data = data.slice(-2);
} }
@ -422,8 +404,30 @@ function filters(path, params) {
} }
} }
// collapse repeated commands
// h 20 h 30 -> h 50
if (
params.collapseRepeated &&
!hasMarkerMid &&
('mhv'.indexOf(instruction) > -1) &&
prev.instruction &&
instruction == prev.instruction.toLowerCase() &&
(
(instruction != 'h' && instruction != 'v') ||
(prev.data[0] >= 0) == (item.data[0] >= 0)
)) {
prev.data[0] += data[0];
if (instruction != 'h' && instruction != 'v') {
prev.data[1] += data[1];
}
prev.coords = item.coords;
if (prev.original) prev.original = null;
path[index] = prev;
return false;
}
// convert curves into smooth shorthands // convert curves into smooth shorthands
if (params.curveSmoothShorthands && prev) { if (params.curveSmoothShorthands && prev.instruction) {
// curveto // curveto
if (instruction === 'c') { if (instruction === 'c') {
@ -523,6 +527,8 @@ function filters(path, params) {
// z resets coordinates // z resets coordinates
relSubpoint[0] = pathBase[0]; relSubpoint[0] = pathBase[0];
relSubpoint[1] = pathBase[1]; relSubpoint[1] = pathBase[1];
if (prev.instruction == 'z') return false;
prev = item;
} }
@ -581,74 +587,15 @@ function convertToMixed(path, params) {
if ( if (
absoluteDataStr.length < relativeDataStr.length && absoluteDataStr.length < relativeDataStr.length &&
!( !(
params.negativeExtraSpace && instruction == prev.instruction && params.negativeExtraSpace &&
instruction == prev.instruction &&
prev.instruction.charCodeAt(0) > 96 &&
absoluteDataStr.length == relativeDataStr.length - 1 && absoluteDataStr.length == relativeDataStr.length - 1 &&
(data[0] < 0 || 0 < data[0] && data[0] < 1 && prev.data[prev.data.length - 1] % 1) (data[0] < 0 || 0 < data[0] && data[0] < 1 && prev.data[prev.data.length - 1] % 1)
) )
) { ) {
if (instruction.toUpperCase() != prev.instruction) { item.instruction = instruction.toUpperCase();
item.instruction = instruction.toUpperCase(); item.data = adata;
item.data = adata;
} else {
prev.data = prev.data.concat(adata);
prev.coords = item.coords;
path[index] = prev;
return false;
}
} else if (instruction == prev.instruction) {
prev.data = prev.data.concat(data);
prev.coords = item.coords;
path[index] = prev;
return false;
}
prev = item;
return true;
});
return path;
}
/**
* Collapse repeated instructions data
*
* @param {Array} path input path data
* @return {Array} output path data
*/
function collapseRepeated(path, params) {
var prev;
path = path.filter(function(item) {
if (
!hasMarkerMid &&
prev &&
item.instruction === prev.instruction &&
(
'Mmz'.indexOf(item.instruction) > -1 ||
'hv'.indexOf(item.instruction) > -1 && (prev.data[0] >= 0) == (item.data[0] >= 0) ||
!params.utilizeAbsolute
)
) {
// increase previous h or v data with current
if ('hv'.indexOf(item.instruction) > -1) {
prev.data[0] += item.data[0];
} else if (item.instruction.toLowerCase() === 'm') {
prev.data[0] += item.data[0];
prev.data[1] += item.data[1];
// concat previous data with current if it is not z
} else if (item.data) {
prev.data = prev.data.concat(item.data);
}
prev.coords = item.coords;
// filter out current item
return false;
} }
prev = item; prev = item;

View File

@ -4,8 +4,14 @@ exports.type = 'perItem';
exports.active = true; exports.active = true;
exports.params = {
collapseRepeated: true,
leadingZero: true,
negativeExtraSpace: true
};
var path2js = require('./_path.js').path2js, var path2js = require('./_path.js').path2js,
relative2absolute = require('./_path.js').relative2absolute; js2path = require('./_path.js').js2path;
/** /**
* Merge multiple Paths into one. * Merge multiple Paths into one.
@ -13,13 +19,13 @@ var path2js = require('./_path.js').path2js,
* @param {Object} item current iteration item * @param {Object} item current iteration item
* @return {Boolean} if false, item will be filtered out * @return {Boolean} if false, item will be filtered out
* *
* @author Kir Belevich * @author Kir Belevich, Lev Solntsev
*/ */
exports.fn = function(item) { exports.fn = function(item, params) {
if (!item.isElem() || item.isEmpty()) return; if (!item.isElem() || item.isEmpty()) return;
var prevContentItem, var prevContentItem = null,
prevContentItemKeys = null; prevContentItemKeys = null;
item.content = item.content.filter(function(contentItem) { item.content = item.content.filter(function(contentItem) {
@ -31,7 +37,7 @@ exports.fn = function(item) {
contentItem.hasAttr('d') contentItem.hasAttr('d')
) { ) {
if (prevContentItemKeys == null) { if (!prevContentItemKeys) {
prevContentItemKeys = Object.keys(prevContentItem.attrs); prevContentItemKeys = Object.keys(prevContentItem.attrs);
} }
@ -41,15 +47,12 @@ exports.fn = function(item) {
return key == 'd' || return key == 'd' ||
prevContentItem.hasAttr(key) && prevContentItem.hasAttr(key) &&
prevContentItem.attr(key).value == contentItem.attr(key).value; prevContentItem.attr(key).value == contentItem.attr(key).value;
}); }),
prevPathJS = path2js(prevContentItem),
curPathJS = path2js(contentItem);
if (equalData) { if (equalData) {
var prevPathJS = prevContentItem.pathJS; js2path(prevContentItem, prevPathJS.concat(curPathJS), params);
if (prevContentItem.pathJS) {
prevPathJS.push.apply(prevPathJS, contentItem.pathJS);
}
prevContentItem.attr('d').value += contentItem.attr('d').value.replace(/m/i, 'M');
return false; return false;
} }
} }

View File

@ -32,11 +32,13 @@ exports.params = {
negativeExtraSpace: true negativeExtraSpace: true
}; };
var relative2absolute = require('./_path.js').relative2absolute, var _path = require('./_path.js'),
computeCubicBoundingBox = require('./_path.js').computeCubicBoundingBox, relative2absolute = _path.relative2absolute,
computeQuadraticBoundingBox = require('./_path.js').computeQuadraticBoundingBox, computeCubicBoundingBox = _path.computeCubicBoundingBox,
applyTransforms = require('./_path.js').applyTransforms, computeQuadraticBoundingBox = _path.computeQuadraticBoundingBox,
js2path = require('./_path.js').js2path, applyTransforms = _path.applyTransforms,
js2path = _path.js2path,
path2js = _path.path2js,
EXTEND = require('whet.extend'); EXTEND = require('whet.extend');
exports.fn = function(data, params) { exports.fn = function(data, params) {
@ -52,7 +54,7 @@ exports.fn = function(data, params) {
var svgElem = item, var svgElem = item,
pathElem = svgElem.content[0], pathElem = svgElem.content[0],
// get absoluted Path data // get absoluted Path data
path = relative2absolute(EXTEND(true, [], pathElem.pathJS)), path = relative2absolute(EXTEND(true, [], path2js(pathElem))),
xs = [], xs = [],
ys = [], ys = [],
cubicСontrolPoint = [0, 0], cubicСontrolPoint = [0, 0],
@ -312,7 +314,7 @@ exports.fn = function(data, params) {
}); });
// save new // save new
pathElem.attr('d').value = js2path(path, params); js2path(pathElem, path, params);
} }
} }

View File

@ -19,7 +19,7 @@
@@@ @@@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<path d="M10 50m10 10"/> <path d="M20 60"/>
<path d="M10 50l10 10"/> <path d="M10 50l10 10"/>
<path d="M10 50l10-20 20 30"/> <path d="M10 50l10-20 20 30"/>
<path d="M10 50l10-20 20 30"/> <path d="M10 50l10-20 20 30"/>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,21 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<path d="m 0,0"/> <path d="m 0,0"/>
<path d="l 0,0"/> <path d="m 0,0l 0,0"/>
<path d="h 0"/> <path d="m 0,0h 0"/>
<path d="v 0"/> <path d="m 0,0v 0"/>
<path d="c 0,0 0,0 0,0 s 0,0 0,0"/> <path d="m 0,0c 0,0 0,0 0,0 s 0,0 0,0"/>
<path d="q 0,0 0,0 t 0,0"/> <path d="m 0,0q 0,0 0,0 t 0,0"/>
<path d="a 25,25 -30 0,1 0,0"/> <path d="m 0,0a 25,25 -30 0,1 0,0"/>
</svg> </svg>
@@@ @@@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<path d="M0 0"/> <path d="M0 0"/>
<path d=""/> <path d="M0 0"/>
<path d=""/> <path d="M0 0"/>
<path d=""/> <path d="M0 0"/>
<path d=""/> <path d="M0 0"/>
<path d=""/> <path d="M0 0"/>
<path d=""/> <path d="M0 0"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 417 B

After

Width:  |  Height:  |  Size: 471 B

View File

@ -9,6 +9,7 @@
<path d="M10 50v30v50"/> <path d="M10 50v30v50"/>
<path d="M10 50L10 80L10 0"/> <path d="M10 50L10 80L10 0"/>
<path d="M10 50L10 10L10 80"/> <path d="M10 50L10 10L10 80"/>
<path d="M10 50l10 10l20 20l10 10"/>
<path d="M10 50L80 50L0 50"/> <path d="M10 50L80 50L0 50"/>
<path d="M10 50L0 50L80 50"/> <path d="M10 50L0 50L80 50"/>
<path d="M10 50L0 50M80 50M30 10L10 80"/> <path d="M10 50L0 50M80 50M30 10L10 80"/>
@ -27,6 +28,7 @@
<path d="M10 50v80"/> <path d="M10 50v80"/>
<path d="M10 50v30V0"/> <path d="M10 50v30V0"/>
<path d="M10 50V10v70"/> <path d="M10 50V10v70"/>
<path d="M10 50l10 10 20 20 10 10"/>
<path d="M10 50h70H0"/> <path d="M10 50h70H0"/>
<path d="M10 50H0h80"/> <path d="M10 50H0h80"/>
<path d="M10 50H0m30-40L10 80"/> <path d="M10 50H0m30-40L10 80"/>

Before

Width:  |  Height:  |  Size: 898 B

After

Width:  |  Height:  |  Size: 980 B

View File

@ -1,7 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<path d="M 0,0 z"/> <path d="M 0,0 z"/>
<path d="M 10,10 z"/> <path d="M 10,10 z"/>
<path d="M 20,20"/> <path d="M 20,20 l 10,10 M 30,0 c 10,0 20,10 20,20"/>
<path d="M 30,30 z"/> <path d="M 30,30 z"/>
<path d="M 30,30 z" fill="#f00"/> <path d="M 30,30 z" fill="#f00"/>
<path d="M 40,40 z"/> <path d="M 40,40 z"/>
@ -11,7 +11,7 @@
@@@ @@@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<path d="M 0,0 zM 10,10 zM 20,20M 30,30 z"/> <path d="M0 0zM10 10zM20 20l10 10M30 0c10 0 20 10 20 20M30 30z"/>
<path d="M 30,30 z" fill="#f00"/> <path d="M 30,30 z" fill="#f00"/>
<path d="M 40,40 zM 50,50 z"/> <path d="M40 40zM50 50z"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 414 B

After

Width:  |  Height:  |  Size: 465 B

View File

@ -18,8 +18,8 @@
@@@ @@@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<path d="M 0,0 zM 10,10 zM 20,20M 30,30 z" fill="#fff" stroke="#333"/> <path d="M0 0zM10 10zM20 20 30 30z" fill="#fff" stroke="#333"/>
<path d="M 30,30 z" fill="#f00"/> <path d="M 30,30 z" fill="#f00"/>
<path d="M 40,40 zM 50,50 zM 40,40M 50,50"/> <path d="M40 40zM50 50zM40 40 50 50"/>
<path d="M 40,40 zM 50,50 zM 40,40M 50,50M 50,50 z" fill="#fff" stroke="#333"/> <path d="M40 40zM50 50zM40 40 50 50 50 50z" fill="#fff" stroke="#333"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 946 B

After

Width:  |  Height:  |  Size: 925 B