Skip to content

Commit

Permalink
feat: add support for svgs with light colored fill (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ghustavh97 committed May 15, 2021
1 parent 7bfb1e9 commit 47aa95d
Show file tree
Hide file tree
Showing 28 changed files with 195 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ module.exports = {
"new-parens": "error",
"newline-after-var": "off",
"newline-before-return": "error",
"newline-per-chained-call": "error",
"newline-per-chained-call": "off",
"no-async-promise-executor": "off",
"no-alert": "error",
"no-array-constructor": "error",
Expand Down
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true
}
113 changes: 97 additions & 16 deletions src/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const Svg2 = require("oslllo-svg2");
const Potrace = require("oslllo-potrace");

const Svg = function (path) {
this.filled = false;
this.path = path;
this.png = new Object();
this.resized = new Object();
Expand All @@ -17,8 +18,8 @@ const Svg = function (path) {

Svg.prototype = {
getResized: function () {
var element = Svg2(this.outerHTML).svg
.resize({
var element = Svg2(this.outerHTML)
.svg.resize({
width: 600,
height: Svg2.AUTO,
})
Expand All @@ -31,33 +32,113 @@ Svg.prototype = {
getOriginal: function () {
var element = this.element.cloneNode(true);
var dimensions = this.svg2.svg.dimensions();
var attributes = Object.values(element.attributes).map(function (attribute) {
return { name: attribute.name, value: attribute.value };
});
var attributes = this.getAttributes(element);

return { element, dimensions, attributes };
},
getScale: function () {
return this.original.dimensions.height / this.resized.dimensions.height;
},
getFirstPathElement(element) {
return element.getElementsByTagName("path")[0];
},
getAttributes(element) {
return Object.values(element.attributes).map(function (attribute) {
/**
* Ignore <path></path> "d" attribute.
*/
if (attribute.name !== "d") {
return { name: attribute.name, value: attribute.value };
}

return false;
});
},
resetAttributes(element, attributes) {
var i = element.attributes.length;
while (i >= 0) {
var attribute = element.attributes[i];
/**
* Ignore <path></path> "d" attribute.
*/
if (attribute && attribute.name !== "d") {
element.removeAttribute(attribute.name);
}
i--;
}
this.setAttributes(element, attributes);
},
valueIsNotBlack(value) {
return value !== "#000" && value !== "black";
},
setAttributes(element, attributes) {
attributes.forEach((attribute) => {
if (attribute) {
if (element.tagName.toLowerCase() === "path") {
if (
(attribute.name === "stroke" && this.valueIsNotBlack(attribute.value)) ||
(attribute.name === "fill" && this.valueIsNotBlack(attribute.value))
) {
element.setAttribute(attribute.name, attribute.value);
}
} else {
element.setAttribute(attribute.name, attribute.value);
}
}
});

if (element.tagName.toLowerCase() === "path") {
[
{ name: "stroke", value: "none" },
{ name: "fill-rule", value: "evenodd" },
].forEach((attr) => {
element.setAttribute(attr.name, attr.value);
});

if (!this.filled) {
element.setAttribute("fill", "black");
}
}
},
toOriginal: function (outerHTML) {
var element = Svg2(outerHTML).toElement();
while (element.attributes.length > 0) {
element.removeAttribute(element.attributes[0].name);
}
for (
var i = 0;
element.attributes.length < this.original.attributes.length;
i++
) {
var attribute = this.original.attributes[i];
element.setAttribute(attribute.name, attribute.value);
this.resetAttributes(element, this.original.attributes);

var originalPath = this.getFirstPathElement(this.original.element);
if (originalPath) {
var path = this.getFirstPathElement(element);
this.resetAttributes(path, this.getAttributes(originalPath));
}

return element.outerHTML;
},
hasFill(el) {
if (Object.prototype.hasOwnProperty.call(el.attributes, "fill")) {
if (el.attributes.fill.value !== "none") {
this.filled = true;

return true;
}
}

return false;
},
setFillBlack(el) {
el.setAttribute("fill", "#000");
},
checkFillState(el) {
var path = this.getFirstPathElement(el);
if (path && this.hasFill(path)) {
this.setFillBlack(path);
} else if (this.hasFill(el)) {
this.setFillBlack(el);
}

return el;
},
process: async function () {
var pngBuffer = await this.resized.svg2.png().toBuffer();
var element = this.checkFillState(this.resized.element.cloneNode(true));
var pngBuffer = await Svg2(element.outerHTML).png({ transparent: false }).toBuffer();
var traced = await Potrace(pngBuffer, { svgSize: this.scale }).trace();
traced = this.toOriginal(traced);

Expand Down
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/add-sharp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/bulb-sharp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/ellipsis-horizontal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/heart-sharp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/mic-off.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/power.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/shuffle-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/shuffle-sharp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/shuffle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/sparkles-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/sparkles-sharp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/sparkles.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/assets/broken-icons/multiple/speedometer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions test/assets/broken-icons/multiple/white-fill-path.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions test/assets/broken-icons/multiple/white-fill-svg-and-path.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions test/assets/broken-icons/multiple/white-fill-svg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions test/assets/broken-icons/multiple/wifi-outline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/assets/images/blank.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/output.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

require("./src/test.output");
require("./src/test.attributes");
require("./src/test.path");
30 changes: 25 additions & 5 deletions test/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";

const path = require("path");
const fs = require("fs-extra");
const Svg2 = require("oslllo-svg2");
const { path2 } = require("./helper");
Expand Down Expand Up @@ -48,17 +49,36 @@ function brokenAndFixedSvgsMatch(brokenSvgPath, fixedSvgPath) {
var resize = { width: 250, height: Svg2.AUTO };
var buffers = await Promise.all(
[brokenSvgPath, fixedSvgPath].map(function (svgPath) {
return Svg2(svgPath).svg.resize(resize).extend(20).png({ transparent: true }).toBuffer();
if (path.extname(svgPath) == ".svg") {
return Svg2(svgPath).svg.resize(resize).extend(20).png({ transparent: false }).toBuffer();
}

return fs.readFileSync(svgPath);
})
);
looksame(buffers[0], buffers[1], { strict: false, tolerance: 3.5 }, (err, results) => {
if (err) {
reject(err);
} else {
resolve({
equal: results.equal,
buffer: { broken: buffers[0], fixed: buffers[1] },
});
looksame.createDiff(
{
reference: buffers[0],
current: buffers[1],
highlightColor: "#ff00ff",
strict: false,
tolerance: 3.5,
},
(error, diff) => {
if (error) {
reject(error);
} else {
resolve({
equal: results.equal,
buffer: { broken: buffers[0], fixed: buffers[1], diff: diff },
});
}
}
);
}
});
});
Expand Down
3 changes: 3 additions & 0 deletions test/src/test.output.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ describe("test.output", () => {
var basename = path.basename(svg);
it(`${basename} matches expected output`, async () => {
var { equal, buffer } = await brokenAndFixedSvgsMatch(broken[index], fixed[index]);
var blank = await brokenAndFixedSvgsMatch(fixed[index], path.resolve("test/assets/images/blank.png"));
if (!equal) {
var filename = path.parse(basename).name;
fs.writeFileSync(failed + "/" + filename + ".broken.png", buffer.broken);
fs.writeFileSync(failed + "/" + filename + ".fixed.png", buffer.fixed);
fs.writeFileSync(failed + "/" + filename + ".diff.png", buffer.diff);
}
assert.isFalse(blank.equal, `SVG ${basename} matched a blank image`);
assert.isTrue(equal, `SVG ${basename} did not match expected output`);
});
});
Expand Down
Loading

0 comments on commit 47aa95d

Please sign in to comment.