diff --git a/PSD2HTML2/lib/json2-min.jsx b/PSD2HTML2/lib/json2-min.jsx new file mode 100644 index 0000000..4cd380f --- /dev/null +++ b/PSD2HTML2/lib/json2-min.jsx @@ -0,0 +1,326 @@ +var JSON; +if (!JSON) { + JSON = {}; +} + +(function () { + 'use strict'; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) + ? this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' + : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' + ? c + : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 + ? '[]' + : gap + ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' + : '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 + ? '{}' + : gap + ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' + : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' + ? walk({'': j}, '') + : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); \ No newline at end of file diff --git a/PSD2HTML2/lib/net.jsx b/PSD2HTML2/lib/net.jsx new file mode 100644 index 0000000..967e268 --- /dev/null +++ b/PSD2HTML2/lib/net.jsx @@ -0,0 +1,109 @@ +(function(){ + PSD = {}; + PSD.net = { + remote:function(conf){ + var opt = { + dataType:"GET", + data:{}, + files:{}, + url:"", + port:80, + header:{ + "Content-Type": "multipart/form-data", + //"Accept": "*/*", + "Content-Length": 99999999999, + //"User-Agent": "PSD2HTML", + "Connection": "Keep-Alive", + "Pragma": "no-cache", + //"Referer": "http://127.0.0.1:8000/" + } + }; + + var dataArr = []; + if(conf.constructor == Object){ + for(var k in conf){ + opt[k] = conf[k]; + } + } + // 协议 + dataArr.push(opt.dataType); + dataArr.push(" ") + if(opt.url.substr(0,7) == "http://") opt.url = opt.url.substr(7); + + var domain = opt.url.split("/")[0] + ":" + opt.port; + //opt.header.Host = domain; + if(opt.url.indexOf("/") < 0){ + dataArr.push("/"); + }else{ + dataArr.push(opt.url.substr(opt.url.indexOf("/"))); + } + dataArr.push(" HTTP/1.1\r\n"); + // 头 + var header = opt.header; + var sp = "---------------------------p2h"+ (Math.random() * 10000 | 0) + +new Date; + + for(var k in header){ + dataArr.push(k); + dataArr.push(": "); + dataArr.push(header[k]); + if(k == "Content-Type") dataArr.push("; boundary="+sp); + dataArr.push("\r\n"); + } + dataArr.push("\r\n"); + // 数据 + for(var k in opt.data){ + dataArr.push("--"+sp+"\r\n"); + dataArr.push("Content-Disposition: form-data; "); + dataArr.push('name="'+k+'"'); + dataArr.push("\r\n\r\n"); + dataArr.push(opt.data[k]+"\r\n"); + //dataArr.push(sp+"\r\n"); + } + // 文件 + for(var k in opt.files){ + dataArr.push("--"+sp+"\r\n"); + dataArr.push("Content-Disposition: form-data; "); + dataArr.push('name="'+k+'";'); + dataArr.push('filename="'+(opt.files[k].fsName.replace(/([^\x00-\xff])/g, function(s){ return encodeURI(s)}))+'"'); + dataArr.push("\r\n\r\n"); + var f = opt.files[k]; + f.encoding = "BINARY"; + f.open('r'); + var str = f.read(); + f.close(); + dataArr.push(str+"\r\n"); + //dataArr.push(sp+"\r\n"); + } + dataArr.push("--"+sp+"--\r\n"); + var dataStr = dataArr.join(""); + $.writeln(dataStr); + + var reply = ""; + + var conn = new Socket(); + + conn.encoding = "binary"; + if (conn.open (domain, "binary")) { + conn.write (dataStr); + reply = conn.read(99999999999); + reply = reply.split("\r\n").join("\n"); + reply = reply.split("\r").join("\n"); + //file.write(reply.substr(reply.indexOf("\n\n")+2)); + //file.close(); + //file.rename(fileName); + conn.close(); + }else{ + alert("CONNECTION TO DATABASE FAILED"); + reply = ""; + } + //$.writeln(reply); + } + } +})(); +PSD.net.remote({ + dataType:"POST", + url:"http://127.0.0.1/upload", + port:8000, + data:{te:"wanxianjia"}, + files:{psd:File("C:/Users/xianjia.wanxj/Desktop/服务介绍页面-我是采购商.png"), txt:File("C:/Users/xianjia.wanxj/Desktop/test.txt")} +}); \ No newline at end of file diff --git a/PSD2HTML2/lib/psd.jsx b/PSD2HTML2/lib/psd.jsx new file mode 100644 index 0000000..9a1d144 --- /dev/null +++ b/PSD2HTML2/lib/psd.jsx @@ -0,0 +1,573 @@ +// @include "json2-min.jsx" +// @include "web-fonts.jsx" + +//setting for app preferences +app.preferences.rulerUnits = Units.PIXELS; +app.preferences.typeUnits = TypeUnits.PIXELS; + + +function PSD(option){ + this.doc = app.activeDocument; + this.docs = app.documents; + this.tree = {name:this.doc.name, width:this.doc.width.value, height:this.doc.height.value, imgCount:0, childs:[]}; + this.textLayers = []; //存储所有的文本图层 + this.contentLayers = []; //存储所有的文本图层和图片 + this.allLayers = []; + this.linkReg = /^[aA]$|^[aA]-/; + this.imgReg = /img/; + this.layers = this.doc.layers; + this.option = { + exportImages: false, //是否对每个图层到处图层 + output: File($.fileName).parent.parent+'/output/' + } + if(option){ + for(k in option){ + this.option[k] = option[k]; + } + } + this._init(); +} + +app.activeDocument.colorSamplers.removeAll(); +PSD.colorSampler = app.activeDocument.colorSamplers.add([UnitValue(0, 'px'), UnitValue(0, 'px')]); + +(function(){ + +var _index = -1, + _sliceCount = 0, + _content = [], + _slices = [], + _exportConfig = new ExportOptionsSaveForWeb(), + // get image extension from export configuration + _getExtension = function(op){ + switch(op.format){ + case SaveDocumentType.JPEG: + return 'jpg'; + case SaveDocumentType.PNG: + return 'png'; + + default: + return 'gif'; + } + }; + + _exportConfig.format = SaveDocumentType.JPEG; + _exportConfig.quality = 60; + + +PSD.fn = PSD.prototype = { + _init: function(){ + this.output = Folder(this.option.output); + !this.output.exists && this.output.create(); + + this.dir = Folder(this.output + '/' + this.getPSDName()); + !this.dir.exists && this.dir.create(); + + //this.createSnapshotOnStart(); + }, + // 遍历所有图层 + parse: function(layers, context, skip){ + layers = layers || this.layers; + + if(this.option.exportImages){ + this.layersImgs = new Folder(this.dir + '/layersImgs/'); + !this.layersImgs.exists && this.layersImgs.create(); + } + + for(var i = layers.length - 1; i >= 0; i--){ + var layer = layers[i]; + this._getLayerInfo(layer, context, skip); + } + }, + getWidth: function(){ + return this.doc.width.value; + }, + getHeight: function(){ + return this.doc.height.value; + }, + getPSDName: function(){ + return this.doc.name.substr (0, this.doc.name.length - 4); + }, + // 获取当前图层text样式 + getEffects: function(){ + var ref = new ActionReference(); + var effects = []; + ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt")); + + var desc = executeActionGet(ref); + if (desc.hasKey( stringIDToTypeID('layerEffects'))){ + var effectDesc = desc.getObjectValue(stringIDToTypeID('layerEffects')); + // first key is scale so skip and start with 1 + for ( var effect = 1; effect < effectDesc.count; effect++ ){ + effects.push(typeIDToStringID(effectDesc.getKey(effect ))); + } + } + return effects; + }, + // 获取当前图层 id + getLayerId: function(){ + var ref = new ActionReference(); + ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt")); + + var desc = executeActionGet(ref); + + if (desc.hasKey( charIDToTypeID('LyrI'))){ + var desc = executeActionGet(ref); + var layerId = desc.getInteger(charIDToTypeID('LyrI')); + return layerId; + }else{ + return -1; + } + }, + _getTextInfo: function(layer){ + var textInfo = {}; + if(!layer.kind || (layer.kind && layer.kind.toString() !== "LayerKind.TEXT")) return null; + + var textItem = layer.textItem; + + textInfo = { + /*color: textItem.color.rgb.hexValue, + contents:textItem.contents, + font: WEBFONTS.getWebFont(textItem.font), + size: Math.round(textItem.size.value), + textType: textItem.kind.toString(), + bold: textItem.fauxBold, + italic: textItem.fauxItalic, + indent: Math.round(textItem.firstLineIndent.value), + underline: textItem.underline == UnderlineType.UNDERLINEOFF ? false : true, + textRange: this.getTextRange(), + position:{x: textItem.position[0].value, y: textItem.position[1].value}, + leftIndent: textItem.leftIndent.value, + rightIndent: textItem.rightIndent.value*/ + }; + if(textItem.kind == TextType.PARAGRAPHTEXT){ + textInfo.width = layer.textItem.width.value; + textInfo.height = layer.textItem.height.value; + + // text justification + switch(textItem.justification.toString()){ + case 'Justification.LEFT': + textInfo.textAlign = 'left'; + break; + case 'Justification.RIGHT': + textInfo.textAlign = 'right'; + break; + case 'Justification.CENTER': + textInfo.textAlign = 'center'; + break; + case 'Justification.CENTERJUSTIFIED': + case 'Justification.FULLYJUSTIFIED': + case 'Justification.LEFTJUSTIFIED': + case 'Justification.RIGHTJUSTIFIED': + textInfo.textAlign = 'justify'; + break; + default: + textInfo.textAlign = 'left'; + + } + } + // line height + if(!textItem.useAutoLeading){ + textInfo.lineHeight = Math.round(textItem.leading.value); + }else{ + try{ + textInfo.lineHeight = Math.round(textItem.autoLeadingAmount) + '%'; + }catch(e){} + } + + return textInfo; + }, + _processTagsFun: { + img: function(layer){ + if(layer.typename === 'LayerSet'){ + layer = layer.merge(); + }else if(layer.typename === 'ArtLayer' && layer.kind.toString () === "LayerKind.TEXT"){ + layer.rasterize(RasterizeType.ENTIRELAYER); //栅格化 + } + + layer.tag = "img"; + return layer; + }, + a: function(layer){ + layer.link = true; + + if(layer.typename === 'LayerSet'){ + layer = layer.merge(); + this._processTagsFun.img(layer); + }else if(layer.typename === 'ArtLayer'){ + + /*if(layer.kind.toString () === "LayerKind.TEXT"){ + var textItem = layer.textItem; + + if(WEBFONTS.indexOf(textItem.font) < 0 || this.getEffects().length > 0 || textItem.warpStyle !== WarpStyle.NONE){ + layer.rasterize(); //栅格化 + this._processTagsFun.img(layer); + } + }*/ + } + + return layer; + } + }, + _processTags: function(tags, layer){ + if(layer.kind && layer.kind.toString () === "LayerKind.TEXT"){ + var textItem = layer.textItem; + + if(WEBFONTS.indexOf(textItem.font) < 0 || this.getEffects().length > 0 || textItem.warpStyle !== WarpStyle.NONE){ + layer.rasterize(); //栅格化 + } + } + + if(tags){ + for(var i = 0, len = tags.length; i < len; i++){ + layer = this._processTagsFun[tags[i].substring (1)].call(this, layer); + } + } + + return layer; + }, + // 获取图层信息 + _getLayerInfo: function(layer, context, skip){ + _index++; + + context = context || this.tree; + + if(layer.visible === false) return "the layer is hidden"; + + try{ + if(skip && skip(layer)) return "skip this layer"; + }catch(e){} + + this.doc.activeLayer = layer; + + /* get layer bounds, fix layer bounds */ + var bounds = layer.bounds, + left = bounds[0].value, + left = left > 0 ? left : 0; + right = bounds[2].value, + right = right < this.doc.width.value ? right : this.doc.width.value, + top = bounds[1].value, + top = top > 0 ? bounds[1].value : 0, + bottom = bounds[3].value, + bottom = bottom < this.doc.height.value ? bottom : this.doc.height.value; + + if(right <= left || top >= bottom){ + return "out of viewport"; + } + var kind = layer.kind ? layer.kind.toString() : layer.typename; + var id = this.getLayerId(); + layer.id = id; + + var child = { + type: layer.typename, + name: layer.name, + visible: layer.visible, + left: left, top: top, + right: right, bottom: bottom, + width: right - left, + height: bottom - top, + kind: kind, + isBackgroundLayer: layer.isBackgroundLayer, + index: id, + id: id + } + + child.textInfo = this._getTextInfo(layer); + + var reg = /#img|#a/g; + var tags = layer.name.match(reg); + + layer = this._processTags(tags, layer); + this.allLayers.push(layer); + + child.tag = layer.tag; + child.link = layer.link; + child.layer = layer; + + if(layer.typename === 'ArtLayer'){ + + _content.push(child); + context.childs.push(child); + + if(layer.kind.toString () === "LayerKind.TEXT"){ + this.contentLayers.push(layer); + this.textLayers.push(layer); + } + + if(layer.tag === "img"){ + this.contentLayers.push(layer); + } + + }else if(layer.typename == 'LayerSet'){ + + var o = {type:layer.typename, name:layer.name, index:_index, childs:[]}; + context.childs.push(o); + this.parse(layer.layers, o, skip); + } + }, + exportPng: function(){ + this.hiddenTextLayers(); + var img= new File(this.dir + "/psd.png"); + var options = new ExportOptionsSaveForWeb(); + options.format = SaveDocumentType.PNG; + options.PNG8 = false; + this.doc.exportDocument (img, ExportType.SAVEFORWEB, options); + //$.writeln(img.length); + this.visibleTextLayers(); + return img; + //this.visibleTextLayers(); + }, + exportImage: function(layer, index){ + this.hiddenTextLayers(); + try{ + var bounds = layer.bounds; + layer.copy(); + layerWidth = UnitValue(bounds[2].value - bounds[0].value, 'px'), + layerHeight = UnitValue(bounds[3].value - bounds[1].value, 'px'); + var newDoc = this.docs.add(layerWidth, layerHeight); + newDoc.paste(); + newDoc.layers[newDoc.layers.length - 1].remove(); + + var img= new File(this.imagesFolder + "/layer_"+_index+".png"); + var options = new ExportOptionsSaveForWeb(); + options.format = SaveDocumentType.PNG; + newDoc.exportDocument (img, ExportType.SAVEFORWEB, options); + newDoc.close(SaveOptions.DONOTSAVECHANGES); + }catch(e){ + alert(e+'#####'+layer.name); + } + this.visibleTextLayers(); + }, + exportJSON: function(data, format){ + var f = new File(this.dir + "/json.txt"); + f.encoding = format || 'UTF-8'; + f.open('w', 'TEXT'); + f.write(JSON.stringify(data || this.tree)); + f.close(); + return f; + }, + getJSON: function(){ + return this.tree; + }, + hiddenTextLayers: function(){ + for(var i = 0, l = this.contentLayers.length; i < l; i++){ + if(!this.contentLayers[i].visible) continue; + this.contentLayers[i].visible = false; + } + }, + visibleTextLayers: function(){ + for(var i = 0, l = this.contentLayers.length; i < l; i++){ + if(this.contentLayers[i].visible) continue; + this.contentLayers[i].visible = true; + } + }, + /* 自动切片并导出图片 */ + autoSliceAndExport: function(exportConfig, height){ + this.hiddenTextLayers(); + + var conf = exportConfig || _exportConfig; + var extension = _getExtension(conf); + + if(!height){ + // 生成测试img,用于计算切片高度 + var testImg = File(this.dir + '/' + 'img.tmp.' + extension); + this.doc.exportDocument (testImg, ExportType.SAVEFORWEB, exportConfig); + var size = testImg.length, HEIGHT = 120; + + if(size < 70000){ + HEIGHT = this.getHeight(); + }else{ + HEIGHT = Math.round(this.getHeight() / Math.ceil(size / 70000)); + } + testImg.remove(); //删除测试img + }else{ + var HEIGHT = height; + } + + var selection = this.doc.selection, + docWidth = this.doc.width.value, + docHeight = this.doc.height.value, + region = [], + y = 0, fy; + + + var slicesFolder = new Folder(this.dir + '/slices/'); + !slicesFolder.exists && slicesFolder.create(); + + try{ + while(y < docHeight){ + _index++; + + y = y + HEIGHT; + fy = y > docHeight ? docHeight : y; + region = [[0, y - HEIGHT], [docWidth, y - HEIGHT], [docWidth, fy], [0, fy]]; + + this.exportSelection(region, exportConfig); + + _slices.push({index:_index, type:"ArtLayer", visible:true, kind:"LayerKind.NORMAL", isBackgroundLayer:false, + name:'slice_'+_index+'.'+extension, right: docWidth, top:y - HEIGHT, left:0, bottom:fy}); + _sliceCount++; + } + selection.deselect(); + }catch(e){ + // TODO + } + this.visibleTextLayers(); + return _slices; + }, + // 导出选择区域 + exportSelection: function(region, exportConfig){ + if(!region) return; + // image config + + var conf = exportConfig || _exportConfig; + var extension = _getExtension(conf); + // copy selected area + var selection = this.doc.selection, xset = [], yset = []; + selection.select(region); + selection.copy(true); + //selection.deselect(); + + for(var i = 0, l = region.length; i < l; i++){ + xset.push(region[i][0]); + yset.push(region[i][1]); + } + // var width = Math.max.apply(null, xset) - Math.min.apply(null, xset), + // height = Math.max.apply(null, yset) - Math.min.apply(null, yset); + xset.sort(function(a,b){return a-b;}); + yset.sort(function(a,b){return a-b;}); + var width = xset[xset.length-1] - xset[0], + height = yset[yset.length-1] - yset[0]; + + // export image + var newDoc = this.docs.add(width, height); + newDoc.paste(); + newDoc.layers[newDoc.layers.length - 1].remove(); + _index++; + + var slicesFolder = new Folder(this.dir + '/slices/'); + !slicesFolder.exists && slicesFolder.create(); + + var img = new File(slicesFolder + "/slice_" + _index + "." + extension); + newDoc.exportDocument (img, ExportType.SAVEFORWEB, conf); + newDoc.close(SaveOptions.DONOTSAVECHANGES); + img.name = 'slice_'+_index+'.'+extension; + return {name:img.name,width:width,height:height}; + }, + getTextLayersAndSlices: function(option, height){ + if(_slices.length <= 0) this.autoSliceAndExport(option, height); + var data = {name: this.doc.name, imgCount:_sliceCount, childs:_slices.concat(_content)}; + //this.exportJSON(data); + return data; + }, + + getTextLayers: function(){ + return _content; + }, + // 获取text range + getTextRange: function(){ + var desc = (function(){ + var ref = new ActionReference(); + ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt")); + return executeActionGet(ref); + })(); + + var list = desc.getObjectValue(charIDToTypeID("Txt ")); + var tsr = list.getList(charIDToTypeID("Txtt")); + var info = []; + + for(var i = 0, l = tsr.count;i < l; i++){ + var tsr0 = tsr.getObjectValue(i) ; + var from = tsr0.getInteger(charIDToTypeID("From")); + var to = tsr0.getInteger(charIDToTypeID("T ")); + var range = [from, to]; + var textStyle = tsr0.getObjectValue(charIDToTypeID("TxtS")); + var font = textStyle.getString(charIDToTypeID("FntN" )); + var size = textStyle.getDouble(charIDToTypeID("Sz " )); + var color = textStyle.getObjectValue(charIDToTypeID('Clr ')); + var bold = textStyle.getBoolean(stringIDToTypeID('syntheticBold')); + var italic = textStyle.getBoolean(stringIDToTypeID('syntheticItalic')); + var underlineValue = textStyle.getEnumerationValue(stringIDToTypeID( "underline" )); + var underline = underlineValue == 1647 ? true : false; + var autoLeading = textStyle.getBoolean(stringIDToTypeID( "autoLeading" )); + var textColor = new SolidColor; + + textColor.rgb.red = color.getDouble(charIDToTypeID('Rd ')); + textColor.rgb.green = color.getDouble(charIDToTypeID('Grn ')); + textColor.rgb.blue = color.getDouble(charIDToTypeID('Bl ')); + var o = {range:range, font:font, size:size, color:textColor.rgb.hexValue, bold:bold, italic:italic, underline:underline}; + if(!autoLeading) o.lineHeight = textStyle.getUnitDoubleValue(charIDToTypeID( "Ldng" )); + info.push(o); + } + return info; + }, + // get pointer (x, y) color + getRGBColor: function(x, y){ + if(!x || !y) return; + PSD.colorSampler.move([UnitValue(x, 'px'), UnitValue(y, 'px')]); + return PSD.colorSampler.color.rgb.hexValue; + }, + selectionIsMonochrome: function(region){ + var selection = this.doc.selection, + cs = this.doc.channels; + + selection.select(region); + + for(var i = 0, l = cs.length; i < l; i++){ + var histogram = cs[i].histogram.concat(); + $.writeln(histogram.join('-')); + histogram.sort().reverse(); + if(histogram[1] != 0) return false; + } + //var his = this.doc.historyStates; + //this.doc.activeHistoryState = his[his.length - 1]; + return true; + }, + reset: function(){ + this.doc.activeHistoryState = this.doc.historyStates.getByName("psdtohtml"); + this.doc.save(); + }, + createSnapshotOnStart: function(){ + var his = this.doc.historyStates; + try{ + // if has psdtohtml snapshot + var snapshot = his.getByName("psdtohtml"); + this.doc.activeHistoryState = snapshot; + // delete it + var idDlt = charIDToTypeID( "Dlt " ); + var desc175 = new ActionDescriptor(); + var idnull = charIDToTypeID( "null" ); + var ref131 = new ActionReference(); + var idHstS = charIDToTypeID( "HstS" ); + var idCrnH = charIDToTypeID( "CrnH" ); + ref131.putProperty( idHstS, idCrnH ); + desc175.putReference( idnull, ref131 ); + executeAction( idDlt, desc175, DialogModes.NO ); + }catch(e){ + + } + // create snapshot + var idMk = charIDToTypeID( "Mk " ); + var desc202 = new ActionDescriptor(); + var idnull = charIDToTypeID( "null" ); + var ref163 = new ActionReference(); + var idSnpS = charIDToTypeID( "SnpS" ); + ref163.putClass( idSnpS ); + desc202.putReference( idnull, ref163 ); + var idFrom = charIDToTypeID( "From" ); + var ref164 = new ActionReference(); + var idHstS = charIDToTypeID( "HstS" ); + var idCrnH = charIDToTypeID( "CrnH" ); + ref164.putProperty( idHstS, idCrnH ); + desc202.putReference( idFrom, ref164 ); + var idNm = charIDToTypeID( "Nm " ); + desc202.putString( idNm, "psdtohtml" ); + var idUsng = charIDToTypeID( "Usng" ); + var idHstS = charIDToTypeID( "HstS" ); + var idFllD = charIDToTypeID( "FllD" ); + desc202.putEnumerated( idUsng, idHstS, idFllD ); + executeAction( idMk, desc202, DialogModes.NO ); + } +} + +})(); diff --git a/PSD2HTML2/lib/web-fonts.jsx b/PSD2HTML2/lib/web-fonts.jsx new file mode 100644 index 0000000..4c422c7 --- /dev/null +++ b/PSD2HTML2/lib/web-fonts.jsx @@ -0,0 +1,17 @@ +var WEBFONTS = ['Arial', 'Arial Narrow', 'ArialMT' , 'Verdana', 'Georgia', 'Times New Roman', +'Trebuchet MS', 'Courier New', 'Impact', 'Comic Sans MS', 'Tahoma', 'Courier', 'Lucida Sans Unicode', +'Lucida Console', 'Garamond', ' MS Sans Serif', 'MS Serif', 'Palatino Linotype', 'Symbol', '宋体','新宋体', '微软雅黑', '黑体', '楷体', +'幼圆', '仿宋', 'MicrosoftYaHei', 'SimSun','NSimSun', 'AdobeSongStd-Light', 'SimHei']; + +WEBFONTS.webFont = {microsoftyahei:'Microsoft YaHei'}; + +WEBFONTS.getWebFont = function(font){ + return WEBFONTS.webFont[font.toLowerCase()] || font; +} + +WEBFONTS.indexOf = function(s){ + for(var i = 0, l = WEBFONTS.length; i < l; i++){ + if(WEBFONTS[i].toLowerCase() === s.toLowerCase()) return i; + } + return -1; +} \ No newline at end of file diff --git a/test/psd-core.jsx b/test/psd-core.jsx new file mode 100644 index 0000000..62ee3b3 --- /dev/null +++ b/test/psd-core.jsx @@ -0,0 +1,10 @@ +#target photoshop +// @include "../PSD2HTML2/lib/psd.jsx" + +(function(){ + var psd = new PSD(); + psd.parse(); + var layers = psd.allLayers; + var textLayers = psd.textLayers; + var contentLayers = psd.contentLayers; +})(); \ No newline at end of file