Mini Kabibi Habibi
/*
* Copyright (c) 2014 Adobe Systems Incorporated. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
// Note: This module should be considered private and may be changed/removed at
// any time with only a bump to the "patch" version number of generator-core.
/* jshint latedef: false */
(function () {
"use strict";
var _origins = [{ x:0, y: 0}],
_classnames = [],
_psd;
function pushOrigin(left, top) {
_origins.push({ x: left, y:top });
}
function popOrigin() {
if (_origins.length > 1) {
_origins.pop();
}
}
function getOrigin() {
return _origins[_origins.length - 1];
}
function fetchWidth(layer) {
var width;
if (layer.bounds) {
width = (layer.bounds.right - layer.bounds.left);
}
return width;
}
function fetchHeight(layer) {
var height;
if (layer.bounds) {
height = (layer.bounds.bottom - layer.bounds.top);
}
return height;
}
function fetchFontFamily(layer) {
if (layer.text && layer.text.textStyleRange) {
return layer.text.textStyleRange.map(function (style) {
return style.textStyle.fontName;
});
}
return undefined;
}
function fetchFontSize(layer) {
if (layer.text && layer.text.textStyleRange) {
return layer.text.textStyleRange.map(function (style) {
return style.textStyle.size.value;
});
}
return undefined;
}
function fetchFontWeight(layer) {
if (layer.text && layer.text.textStyleRange) {
return layer.text.textStyleRange.map(function (style) {
var styleName = style.textStyle.fontStyleName;
styleName = /bold/i.exec(styleName);
if (styleName) {
return styleName[0].toLowerCase();
}
return "normal";
});
}
return undefined;
}
function fetchFontStyle(layer) {
if (layer.text && layer.text.textStyleRange) {
return layer.text.textStyleRange.map(function (style) {
var styleName = style.textStyle.fontStyleName;
styleName = /italic/i.exec(styleName);
if (styleName) {
return styleName[0].toLowerCase();
}
return "normal";
});
}
return undefined;
}
function fetchFontAlign(layer) {
if (layer.text && layer.text.paragraphStyleRange) {
return layer.text.paragraphStyleRange.map(function (style) {
if ((style.paragraphStyle !== undefined) && (style.paragraphStyle.align !== undefined)) {
return style.paragraphStyle.align.toLowerCase();
}
return "left";
});
}
return undefined;
}
function fetchBorderRadius(layer) {
var corners = [];
var path = layer.path;
if (path &&
path.pathComponents &&
path.pathComponents[0].origin.type === "roundedRect") {
var radii = path.pathComponents[0].origin.radii;
var topRight = radii[0];
var bottomRight = radii[1];
var bottomLeft = radii[2];
var topLeft = radii[3];
if (topLeft === topRight &&
topLeft === bottomRight &&
topLeft === bottomLeft) {
// Most common case: all the same
corners = [topLeft];
} else {
// For now, specify all four corners in all other cases
corners = [topLeft, topRight, bottomLeft, bottomRight];
}
}
return corners;
}
function fetchOpacity(layer) {
var opacity;
if (layer.blendOptions &&
layer.blendOptions.opacity) {
opacity = layer.blendOptions.opacity.value / 100;
}
return opacity;
}
function decamelcase(string) {
return string.replace(/([A-Z])/g, "-$1").toLowerCase();
}
function fetchBlendMode(layer) {
var blendMode;
if (layer.blendOptions &&
layer.blendOptions.mode) {
blendMode = layer.blendOptions.mode;
blendMode = decamelcase(blendMode);
}
if (blendMode === "pass-Through") {
return "normal";
}
return blendMode;
}
function getRGBColor(input, divide) {
if (divide === undefined) {
divide = 255;
}
return {
"type": "rgb",
"red": input.red ? (input.red / divide) : 0,
"green": input.green ? (input.green / divide) : 0,
"blue": input.blue ? (input.blue / divide) : 0
};
}
function fetchBackgroundColor(layer) {
var bgcolor;
if (!layer.fill) {
return {};
}
var fill = layer.fill;
function getColorStops(colors) {
return colors.map(function (color) {
var stop = {};
stop.position = color.location / 4096.0;
stop.color = getRGBColor(color.color);
stop.midpoint = color.midpoint / 100;
return stop;
});
}
function getAlphaStops(colors) {
return colors.map(function (color) {
var stop = {};
stop.position = color.location / 4096.0;
stop.alpha = color.opacity.value / 100;
stop.midpoint = color.midpoint / 100;
return stop;
});
}
if (fill.class === "solidColorLayer") {
bgcolor = getRGBColor(fill.color);
} else if (fill.class === "gradientLayer") {
bgcolor = {
"type": "angle-gradient",
"gradientType": fill.type,
"angle": 270 - (fill.angle ? fill.angle.value : 0),
"colorstops": getColorStops(fill.gradient.colors),
"alphastops": getAlphaStops(fill.gradient.transparency)
};
/* jshint bitwise: false */
if ((fill.reverse !== true) ^ (fill.type === "radial")) {
bgcolor.colorstops.reverse().forEach(function (s) {
s.position = 1 - s.position; s.midpoint = 1 - s.midpoint;
});
bgcolor.alphastops.reverse().forEach(function (s) {
s.position = 1 - s.position; s.midpoint = 1 - s.midpoint;
});
}
}
return bgcolor;
}
function fetchFontColor(layer) {
if (layer.text && layer.text.textStyleRange) {
return layer.text.textStyleRange.map(function (style) {
var color = style.textStyle.color;
if (color !== undefined) {
return getRGBColor(color);
}
return getRGBColor({});
});
}
return undefined;
}
function fetchTop(layer) {
var top;
if (layer.bounds) {
var o = getOrigin();
top = layer.bounds.top - o.y;
}
return top;
}
function fetchLeft(layer) {
var left;
if (layer.bounds) {
var o = getOrigin();
left = layer.bounds.left - o.x;
}
return left;
}
function fetchEffects(layer) {
if (layer.layerEffects === undefined) {
return undefined;
}
var retval = [];
var shadow = {};
var effect;
if (layer.layerEffects.innerShadow !== undefined &&
layer.layerEffects.innerShadow.enabled) {
retval.push(shadow);
effect = layer.layerEffects.innerShadow;
shadow.type = "inner-shadow";
if (effect.mode === undefined) {
shadow.mode = "multiply";
} else {
shadow.blendMode = effect.mode;
}
if (effect.opacity === undefined) {
shadow.opacity = 0.75;
} else {
shadow.opacity = effect.opacity.value / 100;
}
shadow.distance = effect.distance;
if (effect.blur === undefined) {
shadow.blur = 5;
} else {
shadow.blur = effect.blur;
}
if (effect.chokeMatte === undefined) {
shadow.spread = 0;
} else {
shadow.spread = effect.chokeMatte * shadow.blur / 100;
}
if (effect.localLightingAngle === undefined) {
shadow.angle = _psd.globalLight.angle;
} else {
shadow.angle = effect.localLightingAngle.value;
}
if (effect.color === undefined) {
shadow.color = getRGBColor({});
} else {
shadow.color = getRGBColor(effect.color);
}
}
if ((layer.layerEffects.dropShadow !== undefined) &&
layer.layerEffects.dropShadow.enabled) {
retval.push(shadow);
effect = layer.layerEffects.dropShadow;
shadow.type = "drop-shadow";
shadow.blendMode = effect.mode;
if (effect.opacity === undefined) {
shadow.opacity = 0.75;
} else {
shadow.opacity = effect.opacity.value / 100;
}
shadow.distance = effect.distance;
if (effect.blur === undefined) {
shadow.blur = 5;
} else {
shadow.blur = effect.blur;
}
if (effect.chokeMatte === undefined) {
shadow.spread = 0;
} else {
shadow.spread = effect.chokeMatte * shadow.blur / 100;
}
if (effect.localLightingAngle === undefined) {
shadow.angle = _psd.globalLight.angle;
} else {
shadow.angle = effect.localLightingAngle.value;
}
if (effect.color === undefined) {
shadow.color = getRGBColor({});
} else {
shadow.color = getRGBColor(effect.color);
}
}
return retval;
}
function fetchFillOpacity(layer) {
if ((layer.blendOptions === undefined) || (layer.blendOptions.fillOpacity === undefined)) {
return undefined;
}
return layer.blendOptions.fillOpacity.value / 100;
}
function fetchStroke(layer) {
if (layer.strokeStyle === undefined) {
return undefined;
}
var s = layer.strokeStyle;
var strokeStyle = {};
strokeStyle.color = getRGBColor(s.strokeStyleContent.color);
strokeStyle.opacity = s.strokeStyleOpacity.value / 100;
strokeStyle.lineWidth = s.strokeStyleLineWidth.value;
switch (s.strokeStyleLineCapType) {
case "strokeStyleRoundCap":
strokeStyle.lineCap = "round"; break;
case "strokeStyleSquareCap":
strokeStyle.lineCap = "square"; break;
//case "strokeStyleButtCap":
default:
strokeStyle.lineCap = "butt";
}
switch (s.strokeStyleLineJoinType) {
case "strokeStyleRoundJoin":
strokeStyle.lineJoin = "round"; break;
case "strokeStylebevelJoin":
strokeStyle.lineJoin = "bevel"; break;
//case "strokeStyleMiterJoin":
default:
strokeStyle.lineJoin = "miter";
}
strokeStyle.miterLimit = s.strokeStyleMiterLimit;
strokeStyle.dashes = s.strokeStyleLineDashSet;
strokeStyle.lineDashOffset = s.strokeStyleLineDashOffset.value;
return strokeStyle;
}
function getCSSName(layer) {
// We want to convert the layer name to a valid CSS IDENT
var l = "layer" + layer.index;
var wsSequence = false;
function _toClass(c) {
var ret = c;
var skip = ".<>[]`~!@#$%^&*() {}|?/\\:;\"\',+";
if (c.trim().length === 0) { // Whitespace?
if (wsSequence === false) {
ret = "-"; // Convert first WS in a sequence to dash
wsSequence = true;
} else {
ret = "";
}
} else {
wsSequence = false;
}
if (skip.indexOf(c) >= 0) {
ret = "";
}
return ret;
}
if (layer.name) {
// Otherwise, lowercase everthing. Collapse 1+ whitespace to dash
l = layer.name.toLowerCase();
var buffer = l.split("");
buffer = buffer.map(_toClass);
l = buffer.join("") + "_" + layer.index;
}
return l;
}
var multiLayerFetchers = {
"name": getCSSName,
"top": fetchTop,
"left": fetchLeft,
"width": fetchWidth,
"height": fetchHeight,
"opacity": fetchOpacity,
"blendMode": fetchBlendMode,
"layerEffects": fetchEffects,
"fillOpacity": fetchFillOpacity
};
var SpecializeFetchers = {
"shapeLayer" : {
"type" : function () { return "shape-layer"; },
"color": fetchBackgroundColor,
"topLeftRadius": function (layer) { return fetchBorderRadius(layer)[0]; },
"topRightRadius": function (layer) { return fetchBorderRadius(layer)[1]; },
"bottomLeftRadius": function (layer) { return fetchBorderRadius(layer)[2]; },
"bottomRightRadius": function (layer) { return fetchBorderRadius(layer)[3]; },
"stroke": fetchStroke
},
"textLayer" : {
"type" : function () { return "text-layer"; },
"font-color": fetchFontColor,
"font-family": fetchFontFamily,
"font-size": fetchFontSize,
"font-weight": fetchFontWeight,
"font-style": fetchFontStyle,
"font-align": fetchFontAlign,
"stroke": fetchStroke
},
"layerSection" : {
"type" : function () { return "group-layer"; },
"layers": function (layer) {
pushOrigin(layer.bounds.left, layer.bounds.top);
var layers = [];
if (layer.layers !== undefined) {
layer.layers.forEach(function (layer) {
var s = extractLayerStyleInfo(layer);
if (s !== undefined) {
layers.push(s);
}
});
}
popOrigin();
return layers;
}
},
"layer" : {
"type" : function () { return "image-layer"; }
},
"backgroundLayer" : {
"type" : function () { return "image-layer"; }
}
};
function extractLayerStyleInfo(layer) {
if (layer.visible === false) {
return undefined;
}
var style = {};
var value;
// extract info common to all layers
for (var property in multiLayerFetchers) {
if (multiLayerFetchers.hasOwnProperty(property)) {
value = multiLayerFetchers[property](layer);
if (value !== undefined) {
style[property] = value;
}
}
}
var layerHandler = SpecializeFetchers[layer.type];
//if (layerHandler === undefined) {
// TODO: error
//} else
for (property in layerHandler) {
if (layerHandler.hasOwnProperty(property)) {
value = layerHandler[property](layer);
if (value !== undefined) {
style[property] = value;
}
}
}
return style;
}
/**
* Return a SON document for the specified document info
*
* @param {Object} psd document retrieved from Generator.getDocumentInfo()
*
* @return {Object} The SON document for the specified Generator document
*
* Note: This API should be considered private and may be changed/removed at any
* time with only a bump to the "patch" version number of generator-core.
* Use at your own risk.
*/
function extractStyleInfo(psd/*, opts*/) {
var SON = {};
var layers = psd.layers;
_classnames = [];
_psd = psd;
SON.layers = [];
layers.forEach(function (layer) {
var s = extractLayerStyleInfo(layer);
if (s !== undefined) {
SON.layers.push(s);
}
});
return SON;
}
exports._extractStyleInfo = extractStyleInfo;
}());