Skip to content

Commit

Permalink
Implemented linear and radial gradients for fill and stroke colors.
Browse files Browse the repository at this point in the history
Fixed radial gradients.

Enable transforms for gradients.

Radial transforms not fully supported. Not sure how to deal with scale for them.

Parse the transforms as floats

Update main.js

Fix item tag generation and substituteUseRef

This should be a return

Parse the style attribute on the stop tag.

Adobe likes making stop-color and stop-opacity attributes. Looks like Inkscape use stye tags.

Typo with an extra parentheses

Fix the extra tag close

Bump version number

So it doesn't load an old version from the browser cache.
  • Loading branch information
winsock committed May 31, 2018
1 parent b0c81db commit fa679d1
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 16 deletions.
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,4 @@ <h4 class="modal-title">Multiple SVG files (<span id="files-count"></span> files
</script>

</body>
</html>
</html>
171 changes: 156 additions & 15 deletions js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ var lastFileName = "";
var lastFileData;
var warnings = [];
var svgStyles = {};
var gradients = {};
var globalSvg;

var clipPathEnabled = false;
Expand Down Expand Up @@ -235,6 +236,10 @@ function recursiveTreeWalk(parent, groupLevel, clipPath) {
}

function preprocessReferences(svg) {
svg.find("defs").children().each(function () {
var current = $(this);
substituteUseRef(svg, current);
});
svg.find("use").each(function () {
var current = $(this);
substituteUseRef(svg, current);
Expand Down Expand Up @@ -272,14 +277,20 @@ function substituteUseRef(parent, current) {
//Find definition in svg
var defs = $(parent).find("[id='" + href.substr(1) + "']");
if (defs.length) {
defs = defs.clone();

//Copy overriding attributes into children
$.each(current.prop("attributes"), function () {
defs.attr(this.name, this.value);
//Copy non-overridden attributes
$.each(defs.prop("attributes"), function () {
var attr = current.attr(this.name)
if (typeof attr !== typeof undefined && attr !== false) {
return;
}
current.attr(this.name, this.value);
});

current.replaceWith(defs);
//Copy children
defs.children().each(function () {
var child = $(this);
current.append(child.clone());
});
} else {
console.warn("Found <use> tag but did not found appropriate block in <defs> for id " + href);
}
Expand All @@ -289,6 +300,10 @@ function substituteUseRef(parent, current) {

function parseGroup(groupTag) {
var transform = groupTag.attr("transform");
if (typeof transform === "undefined") {
transform = groupTag.attr("gradientTransform");
}

var id = groupTag.attr("id");
var groupTransform = {transformX: 0, transformY: 0, scaleX: 1, scaleY: 1,
rotate:0, rotatePivotX:-1, rotatePivotY:-1, id:"", isSet:false, clipPathId:null};
Expand Down Expand Up @@ -520,11 +535,11 @@ function printPath(pathData, stylesArray, groupLevel, clipPath) {
}

//If strokeWidth is needed but omitted, default to 1
var needsStrokeWidth = (typeof styles["stroke"] !== "undefined") ||
(typeof styles["stroke-opacity"] !== "undefined") ||
(typeof styles["stroke-alpha"] !== "undefined") ||
(typeof styles["stroke-linejoin"] !== "undefined") ||
(typeof styles["stroke-miterlimit"] !== "undefined") ||
var needsStrokeWidth = (typeof styles["stroke"] !== "undefined") ||
(typeof styles["stroke-opacity"] !== "undefined") ||
(typeof styles["stroke-alpha"] !== "undefined") ||
(typeof styles["stroke-linejoin"] !== "undefined") ||
(typeof styles["stroke-miterlimit"] !== "undefined") ||
(typeof styles["stroke-linecap"] !== "undefined");
if (needsStrokeWidth && (typeof styles["stroke-width"] === "undefined")) {
styles["stroke-width"] = "1";
Expand All @@ -534,16 +549,37 @@ function printPath(pathData, stylesArray, groupLevel, clipPath) {
if (!clipPath) {
generatedOutput += INDENT.repeat(groupLevel + 1) + '<path\n';
if (toBool(localStorage.useIdAsName)) generatedOutput += generateAttr('name', styles["id"], groupLevel, "");
generatedOutput += generateAttr('fillColor', parseColorToHex(styles["fill"]), groupLevel, "none");
var gradientID = null;
var gradientAttr = null;
if ((typeof styles["fill"] !== "undefined") && styles["fill"].startsWith("url(")) {
gradientID = styles["fill"].slice(5, -1);
gradientAttr = "fillColor";
} else {
generatedOutput += generateAttr('fillColor', parseColorToHex(styles["fill"]), groupLevel, "none");
}
generatedOutput += generateAttr('fillAlpha', styles["fill-opacity"], groupLevel, "1");
generatedOutput += generateAttr('fillType', styles["fill-rule"], groupLevel, "nonZero");
generatedOutput += generateAttr('strokeColor', parseColorToHex(styles["stroke"]), groupLevel, "none");
if ((typeof styles["stroke"] !== "undefined") && styles["stroke"].startsWith("url(")) {
gradientID = styles["stroke"].slice(5, -1);
gradientAttr = "strokeColor";
} else {
generatedOutput += generateAttr('strokeColor', parseColorToHex(styles["stroke"]), groupLevel, "none");
}
generatedOutput += generateAttr('strokeAlpha', styles["stroke-opacity"], groupLevel, "1");
generatedOutput += generateAttr('strokeWidth', removeNonNumeric(styles["stroke-width"]), groupLevel, "0");
generatedOutput += generateAttr('strokeLineJoin', styles["stroke-linejoin"], groupLevel, "miter");
generatedOutput += generateAttr('strokeMiterLimit', styles["stroke-miterlimit"], groupLevel, "4");
generatedOutput += generateAttr('strokeLineCap', styles["stroke-linecap"], groupLevel, "butt");
generatedOutput += generateAttr('pathData', pathData, groupLevel, null, true);
if (gradientID !== null) {
generatedOutput += generateAttr('pathData', pathData, groupLevel, null);
generatedOutput += '>\n';
generatedOutput += INDENT.repeat(groupLevel + 2) + '<aapt:attr name="android:' + gradientAttr + '">\n';
generatedOutput += gradients[gradientID] + '\n';
generatedOutput += INDENT.repeat(groupLevel + 2) + '</aapt:attr>\n';
generatedOutput += INDENT.repeat(groupLevel + 1) + '</path>\n';
} else {
generatedOutput += generateAttr('pathData', pathData, groupLevel, null, true);
}
pathsParsedCount++;
} else {
clipPathMerged.push(pathData);
Expand All @@ -555,6 +591,104 @@ function printPath(pathData, stylesArray, groupLevel, clipPath) {
}
}

function parseGradients(svg) {
svg.find("defs").children().each(function () {
var current = $(this);
if (current.prop("tagName").endsWith("Gradient")) {
var transform = parseGroup(current);
if (typeof transform.transformX === "string") {
transform.transformX = parseFloat(transform.transformX);
}
if (typeof transform.transformY === "string") {
transform.transformX = parseFloat(transform.transformX);
}
if (typeof transform.scaleX === "string") {
transform.scaleX = parseFloat(transform.scaleX);
}
if (typeof transform.scaleY === "string") {
transform.scaleY = parseFloat(transform.scaleY);
}

var androidXML = '<gradient\n';
androidXML += generateAttr('angle', transform.rotate, 1, 0);
if (current.prop("tagName").startsWith("linear")) {
var x1 = 0.0;
if (typeof current.attr("x1") !== "undefined") {
x1 = parseFloat(current.attr("x1")) + transform.transformX;
}
var y1 = 0.0;
if (typeof current.attr("y1") !== "undefined") {
y1 = parseFloat(current.attr("y1")) + transform.transformY;
}
var x2 = 0.0;
if (typeof current.attr("x2") !== "undefined") {
x2 = parseFloat(current.attr("x2")) + transform.transformX;
}
var y2 = 0.0;
if (typeof current.attr("y2") !== "undefined") {
y2 = parseFloat(current.attr("y2")) + transform.transformY;
}

var width = Math.abs(x2 - x1);
var height = Math.abs(y2 - y1);
var newWidth = width * transform.scaleX;
var newHeight = height * transform.scaleY;
var dX = (width - newWidth) / 2;
var dY = (height - newHeight) / 2;
if (x1 < x2) {
x1 += dX;
x2 -= dX;
} else {
x1 -= dX;
x2 += dX;
}
if (y1 < y2) {
y1 += dY;
y2 -= dY;
} else {
y1 -= dY;
y2 += dY;
}

androidXML += generateAttr('startX', x1, 1, 0.0);
androidXML += generateAttr('startY', y1, 1, 0.0);
androidXML += generateAttr('endX', x2, 1, 0.0);
androidXML += generateAttr('endY', y2, 1, 0.0);
androidXML += INDENT.repeat(1) + 'android:type="linear">';
} else if (current.prop("tagName").startsWith("radial")) {
androidXML += generateAttr('centerX', parseFloat(current.attr("cx")) + transform.transformX, 1, 0.0);
androidXML += generateAttr('centerY', parseFloat(current.attr("cy")) + transform.transformY, 1, 0.0);
androidXML += generateAttr('gradientRadius', current.attr("r"), 1, "0");
androidXML += INDENT.repeat(1) + 'android:type="radial">';
}
current.children().each(function () {
var stopTag = $(this);
var stopStyles = parseStyles(stopTag);
if (typeof stopTag.attr("stop-color") !== "undefined") {
stopStyles["stop-color"] = stopTag.attr("stop-color");
}
if (typeof stopTag.attr("stop-opacity") !== "undefined") {
stopStyles["stop-opacity"] = stopTag.attr("stop-opacity");
}
androidXML += INDENT.repeat(2) + '<item\n';
androidXML += generateAttr('offset', stopTag.attr("offset"), 3, null);
if (typeof stopStyles["stop-opacity"] !== "undefined") {
var alpha = ("0" + Math.round(parseFloat(stopStyles["stop-opacity"]) * 255).toString(16)).slice(-2)
var color = parseColorToHex(stopStyles["stop-color"]).slice(-6)

androidXML += generateAttr('color', "#"+alpha+color, 3, null, true);
} else if (typeof stopStyles["stop-color"] !== "undefined") {
androidXML += generateAttr('color', parseColorToHex(stopStyles["stop-color"]), 3, null, true);
} else {
androidXML += " />";
}
});
androidXML += '</gradient>';
gradients[current.attr("id")] = androidXML;
}
});
}

function generateCode(inputXml) {
var resultData = { error:null, warnings:null, code:null };

Expand All @@ -574,6 +708,7 @@ function generateCode(inputXml) {
var svg = xml.find("svg");
globalSvg = svg;
preprocessReferences(svg);
parseGradients(svg);

if (toBool(localStorage.bakeTransforms)) {
try {
Expand All @@ -598,6 +733,7 @@ function generateCode(inputXml) {
//XML Vector start
generatedOutput = '<?xml version="1.0" encoding="utf-8"?>\n';
generatedOutput += '<vector xmlns:android="http://schemas.android.com/apk/res/android"';
generatedOutput += '\n' + INDENT + 'xmlns:aapt="http://schemas.android.com/aapt"'

generatedOutput += '\n' + INDENT + 'android:width="{0}dp"\n'.f(width);
generatedOutput += INDENT + 'android:height="{0}dp"\n'.f(height);
Expand Down Expand Up @@ -832,6 +968,11 @@ function parseColorToHex(color) {

//Is hex already
if (color.substr(0, 1) === "#") {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
color = color.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
return color;
} else {
if (color.startsWith("rgb(")) {
Expand Down Expand Up @@ -890,4 +1031,4 @@ function showLastUpdate(repo) {
}
});
});
}
}

0 comments on commit fa679d1

Please sign in to comment.