背景:身在一個有實業的電商公司,設計部的妹子們總是會有做不完的商品圖片,當然了,要是做點有技術含量的美化工作也罷,但是最近她們很是無聊,總是要做一些重復性的工作,就比如如題所說的,圖片量產,量產什么呢?價格牌。。。這東西很沒意思哎!就是給你一個模板,然后你自己把模板原來的文字圖片換掉就行了,再排一下版,純體力勞動好么!博主做過一陣子的對日外包工作,深知她們的痛苦,如果說某些對日外包的程序猿是人肉轉碼器的話,那么設計部的妹子們現在就成了。。算了,詞就不說了,太殘酷了
========================================炫炸天的分割線========================================
線索:針對背景交代的情況,BOSS給了我一個提示:PS腳本,順着這個線索,我就進行了一系列調查,我分別做了@#¥%……&*()的努力,簡而言之,photoShop自帶的開發文檔幫了我大忙,文檔位於Adobe Photoshop CS5\Scripting\Documents\,參考了Photoshop CS5 Scripting Guide.pdf和Photoshop CS5 JavaScript Ref.pdf,當然了,自帶的文檔貌似還不能完全滿足我的需求,我還自己下載了一個JavaScriptToolsGuide_CS5.pdf,抱着這三個文檔啃了又啃,總歸算是完成了這樣一個量產工具。
========================================酷炸天的分割線========================================
開發思路:既然是量產工具,那么他的工作流程應該是這樣的,1工具讀了一個文件,文件包含了所有產品的信息,2循環抽取每一個商品的信息,生成圖片並保存,3循環完畢,給出一個提示,OK,大概思路就是這樣了,然后接下來就是順着這個思路啃文檔了。。。
========================================拽炸天的分割線========================================
代碼如下:
1 //測試版本:PhotoShop CS5 12.0.3 x32 通過 2 //作者:Duke 3 //測試完結日期:2014/08/24 4 5 priceCardGenerator() 6 7 function priceCardGenerator(){ 8 9 /** 10 * 定義統一的賦值變量 11 */ 12 var title //標題 13 var liwaiCode //里外編碼 14 var priceTitle //售價標題,為了方便替換 15 var price //售價 16 var sample //價格符號 17 var fiveAssureFeeTitle //五包費用標題,為方便替換 18 var fiveAssureFee //五包費用 19 var seftFeeTitle //自提費用標題,為方便替換 20 var seftFee //自提費用 21 var size //尺寸 22 var producingArea //產地 23 var material //材質 24 25 /** 26 * 定義統一的字體配置 27 */ 28 var tipFont = "MicrosoftYaHei-Bold" 29 var valueFont = "MicrosoftYaHei" 30 var priceFont = "Century Gothic" 31 32 /** 33 * 定義統一的顏色配置 34 */ 35 //價格的顏色 36 var priceColor = new SolidColor() 37 priceColor.rgb.red = 208 38 priceColor.rgb.green = 28 39 priceColor.rgb.blue = 119 40 //頂部欄的顏色 41 var topBarColor = new SolidColor() 42 topBarColor.rgb.red = 212 43 topBarColor.rgb.green = 0 44 topBarColor.rgb.blue = 102 45 46 /** 47 * 在此讀取文件 48 */ 49 //打開logo圖片 50 var logoImg = app.open(File("D:/priceCard/liwailogo.jpg")) 51 52 //讀取數據文件 53 var dataFile = new File("D:/priceCard/priceCardData.xml") 54 //后續操作設置為“讀”操作 55 dataFile.open("r") 56 57 //緩沖變量 58 var xmlCode = dataFile.read() 59 //alert(xmlCode) 60 61 //空文件直接退出 62 if(xmlCode == ""){ 63 alert("文件沒有內容") 64 return 65 } 66 67 //新建XML對象 68 var products = new XML(xmlCode) 69 70 //產品總數 71 var productCount = products.product.length() 72 73 //遍歷 74 for( i = 0;i < productCount;i++){ 75 76 //變量賦值 77 title = products.product[i].elements()[0] 78 liwaiCode = "里外編碼:" + products.product[i].elements()[1] 79 priceTitle = "售價" 80 price = products.product[i].elements()[2] 81 sample = "¥" 82 fiveAssureFeeTitle = "五包費用" 83 fiveAssureFee = "¥" + products.product[i].elements()[3] 84 seftFeeTitle = "自提費用" 85 seftFee = "¥" + products.product[i].elements()[4] 86 size = products.product[i].elements()[5] 87 producingArea = products.product[i].elements()[6] 88 material = products.product[i].elements()[7] 89 90 // 存儲當前的單位長度,並設置自定義的單位 91 var originalUnit = preferences.rulerUnits 92 preferences.rulerUnits = Units.PIXELS 93 94 // 聲明一個文檔 95 var docRef = app.documents.add( 886, 561 ,72.0,"tempDoc") 96 97 // 頂部欄,創建選區並上色 98 // “選區”的填充要在定義組之前操作,否則會報出“fill方法在當前版本不可用” 99 docRef.selection.select([[0,0],[0,20],[886,20],[886,0]],SelectionType.EXTEND) 100 var selRef = docRef.selection 101 selRef.fill( topBarColor, ColorBlendMode.NORMAL, 100, false) 102 103 // 定義一個圖片組 104 var layerSetRef =docRef.layerSets.add() 105 layerSetRef.name = "圖片組" 106 107 //設置logo所在的文檔為活動文檔 108 app.activeDocument = logoImg 109 //聲明logo圖層 110 var logoLayer = logoImg.activeLayer 111 //復制商品圖層到背景文檔 112 var logoLayerTemp = logoLayer.duplicate(layerSetRef, 113 ElementPlacement.PLACEATEND) 114 //設置背景文檔為活動文檔 115 app.activeDocument=docRef 116 //logo移動至左下角 117 logoLayerTemp.translate(-265,225) 118 119 //讀取當前商品對應的二維碼圖片 120 var qrCodeImg = app.open(File(products.product[i].elements()[9])) 121 //設置二維碼圖片所在的文檔為活動文檔 122 app.activeDocument = qrCodeImg 123 //聲明二維碼圖片圖層 124 var qrCodeImgLayer = qrCodeImg.activeLayer 125 //復制二維碼圖片到背景文檔 126 var qrCodeImgLayerTemp = qrCodeImgLayer.duplicate(layerSetRef, 127 ElementPlacement.PLACEATEND) 128 //設置背景文檔為活動文檔 129 app.activeDocument=docRef 130 //商品圖片移動至中間偏右 131 qrCodeImgLayerTemp.translate(320,180) 132 //關閉商品圖片文檔 133 qrCodeImg.close(SaveOptions.DONOTSAVECHANGES) 134 135 //讀取當前價格牌的商品圖片 136 var productImg = app.open(File(products.product[i].elements()[8])) 137 //設置商品圖片所在的文檔為活動文檔 138 app.activeDocument = productImg 139 //聲明商品圖片圖層 140 var productImgLayer = productImg.activeLayer 141 //復制商品圖層到背景文檔 142 var productImgLayerTemp = productImgLayer.duplicate(layerSetRef, 143 ElementPlacement.PLACEATEND) 144 //設置背景文檔為活動文檔 145 app.activeDocument=docRef 146 //商品圖片移動至中間偏右 147 productImgLayerTemp.translate(200,-50) 148 //關閉商品圖片文檔 149 productImg.close(SaveOptions.DONOTSAVECHANGES) 150 151 /** 152 *內容開始 153 */ 154 // 商品名稱 155 var proNameLayerRef = docRef.artLayers.add() 156 proNameLayerRef.kind = LayerKind.TEXT 157 var proNameTextItemRef = proNameLayerRef.textItem 158 proNameTextItemRef.contents = title 159 proNameTextItemRef.position = Array(55, 70) 160 proNameTextItemRef.font = tipFont 161 proNameTextItemRef.size = 30 162 163 //里外編碼 164 var liwaiCodeLayerRef = docRef.artLayers.add() 165 liwaiCodeLayerRef.kind = LayerKind.TEXT 166 var liwaiCodeTextItemRef = liwaiCodeLayerRef.textItem 167 liwaiCodeTextItemRef.contents = liwaiCode 168 liwaiCodeTextItemRef.position = Array(55, 95) 169 liwaiCodeTextItemRef.font = valueFont 170 liwaiCodeTextItemRef.size = 14 171 172 //售價標題 173 var liwaiCodeLayerRef = docRef.artLayers.add() 174 liwaiCodeLayerRef.kind = LayerKind.TEXT 175 var liwaiCodeTextItemRef = liwaiCodeLayerRef.textItem 176 liwaiCodeTextItemRef.contents = priceTitle 177 liwaiCodeTextItemRef.position = Array(55, 135) 178 liwaiCodeTextItemRef.font = tipFont 179 liwaiCodeTextItemRef.size = 22 180 181 //¥ 182 var sampleLayerRef = docRef.artLayers.add() 183 sampleLayerRef.kind = LayerKind.TEXT 184 var sampleTextItemRef = sampleLayerRef.textItem 185 sampleTextItemRef.contents = sample 186 sampleTextItemRef.position = Array(50, 235) 187 sampleTextItemRef.font = tipFont 188 sampleTextItemRef.size = 40 189 sampleTextItemRef.color = priceColor 190 191 //金額 192 var liwaiPriceLayerRef = docRef.artLayers.add() 193 liwaiPriceLayerRef.kind = LayerKind.TEXT 194 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 195 liwaiPriceTextItemRef.contents = price 196 liwaiPriceTextItemRef.position = Array(105, 235) 197 liwaiPriceTextItemRef.font = valueFont 198 liwaiPriceTextItemRef.size = 100 199 liwaiPriceTextItemRef.color = priceColor 200 201 //五包費用 202 var liwaiPriceLayerRef = docRef.artLayers.add() 203 liwaiPriceLayerRef.kind = LayerKind.TEXT 204 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 205 liwaiPriceTextItemRef.contents = fiveAssureFeeTitle 206 liwaiPriceTextItemRef.position = Array(55, 275) 207 liwaiPriceTextItemRef.font = tipFont 208 liwaiPriceTextItemRef.size = 20 209 210 //五包費用金額 211 var liwaiPriceLayerRef = docRef.artLayers.add() 212 liwaiPriceLayerRef.kind = LayerKind.TEXT 213 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 214 liwaiPriceTextItemRef.contents = fiveAssureFee 215 liwaiPriceTextItemRef.position = Array(140, 275) 216 liwaiPriceTextItemRef.font = valueFont 217 liwaiPriceTextItemRef.size = 18 218 liwaiPriceTextItemRef.color = priceColor 219 220 //自提費用 221 var liwaiPriceLayerRef = docRef.artLayers.add() 222 liwaiPriceLayerRef.kind = LayerKind.TEXT 223 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 224 liwaiPriceTextItemRef.contents = seftFeeTitle 225 liwaiPriceTextItemRef.position = Array(250, 275) 226 liwaiPriceTextItemRef.font = tipFont 227 liwaiPriceTextItemRef.size = 20 228 229 //自提費用金額 230 var liwaiPriceLayerRef = docRef.artLayers.add() 231 liwaiPriceLayerRef.kind = LayerKind.TEXT 232 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 233 liwaiPriceTextItemRef.contents = seftFee 234 liwaiPriceTextItemRef.position = Array(335, 275) 235 liwaiPriceTextItemRef.font = valueFont 236 liwaiPriceTextItemRef.size = 18 237 liwaiPriceTextItemRef.color = priceColor 238 239 //規格 240 var liwaiPriceLayerRef = docRef.artLayers.add() 241 liwaiPriceLayerRef.kind = LayerKind.TEXT 242 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 243 liwaiPriceTextItemRef.contents = "規格:" 244 liwaiPriceTextItemRef.position = Array(55, 345) 245 liwaiPriceTextItemRef.font = tipFont 246 liwaiPriceTextItemRef.size = 20 247 248 //規格數值 249 var liwaiPriceLayerRef = docRef.artLayers.add() 250 liwaiPriceLayerRef.kind = LayerKind.TEXT 251 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 252 liwaiPriceTextItemRef.contents = size 253 liwaiPriceTextItemRef.position = Array(125, 345) 254 liwaiPriceTextItemRef.font = valueFont 255 liwaiPriceTextItemRef.size = 20 256 257 //產地 258 var liwaiPriceLayerRef = docRef.artLayers.add() 259 liwaiPriceLayerRef.kind = LayerKind.TEXT 260 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 261 liwaiPriceTextItemRef.contents = "產地:" 262 liwaiPriceTextItemRef.position = Array(55, 375) 263 liwaiPriceTextItemRef.font = tipFont 264 liwaiPriceTextItemRef.size = 20 265 266 //產地值 267 var liwaiPriceLayerRef = docRef.artLayers.add() 268 liwaiPriceLayerRef.kind = LayerKind.TEXT 269 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 270 liwaiPriceTextItemRef.contents = producingArea 271 liwaiPriceTextItemRef.position = Array(125, 375) 272 liwaiPriceTextItemRef.font = valueFont 273 liwaiPriceTextItemRef.size = 20 274 275 //材質 276 var liwaiPriceLayerRef = docRef.artLayers.add() 277 liwaiPriceLayerRef.kind = LayerKind.TEXT 278 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 279 liwaiPriceTextItemRef.contents = "材質:" 280 liwaiPriceTextItemRef.position = Array(55, 405) 281 liwaiPriceTextItemRef.font = tipFont 282 liwaiPriceTextItemRef.size = 20 283 284 //材質值 285 var liwaiPriceLayerRef = docRef.artLayers.add() 286 liwaiPriceLayerRef.kind = LayerKind.TEXT 287 liwaiPriceLayerRef.textItem.kind = TextType.PARAGRAPHTEXT 288 var liwaiPriceTextItemRef = liwaiPriceLayerRef.textItem 289 liwaiPriceTextItemRef.contents = material 290 liwaiPriceTextItemRef.position = Array(125, 391) 291 liwaiPriceTextItemRef.font = valueFont 292 liwaiPriceTextItemRef.width = 550 293 liwaiPriceTextItemRef.height = 200 294 liwaiPriceTextItemRef.size = 20 295 296 //保存文件 297 new Folder("D:/priceCard/result").create () 298 jpgFile = new File( "D:/priceCard/result/" + products.product[i].elements()[1] + ".jpeg" ) 299 jpgSaveOptions = new JPEGSaveOptions() 300 jpgSaveOptions.embedColorProfile = true 301 jpgSaveOptions.formatOptions = FormatOptions.STANDARDBASELINE 302 jpgSaveOptions.matte = MatteType.NONE 303 jpgSaveOptions.quality = 12 304 app.activeDocument.saveAs(jpgFile, jpgSaveOptions, true, 305 Extension.LOWERCASE) 306 307 //強制關閉 308 docRef.close(SaveOptions.DONOTSAVECHANGES) 309 310 //恢復默認長度單位 311 app.preferences.rulerUnits = originalUnit 312 } 313 //關閉logo所在的文檔 314 logoImg.close(SaveOptions.DONOTSAVECHANGES) 315 alert("生成" + productCount + "條產品信息,請在以下位置\"D:/priceCard/result/\"查看") 316 }
代碼里的priceCardData.xml的文檔格式是下面這個樣子的
<products> <product> <!--模板文件禁止修改--> <產品名稱></產品名稱> <!--里外編碼--> <里外編碼></里外編碼> <!--售價--> <售價></售價> <!--五包費用--> <五包費用></五包費用> <!--自提費用--> <自提費用></自提費用> <!--規格大小--> <規格大小></規格大小> <!--產地--> <產地></產地> <!--材質--> <材質></材質> <!--產品圖片地址--> <產品圖片地址></產品圖片地址> <!--產品二維碼地址--> <產品二維碼地址></產品二維碼地址> </product> <product> <!--模板文件禁止修改--> <產品名稱></產品名稱> <!--里外編碼--> <里外編碼></里外編碼> <!--售價--> <售價></售價> <!--五包費用--> <五包費用></五包費用> <!--自提費用--> <自提費用></自提費用> <!--規格大小--> <規格大小></規格大小> <!--產地--> <產地></產地> <!--材質--> <材質></材質> <!--產品圖片地址--> <產品圖片地址></產品圖片地址> <!--產品二維碼地址--> <產品二維碼地址></產品二維碼地址> </product> </products>
看完了這個文檔格式,你應該會有以下的疑問:
1:博主逗比吧,XML標簽怎么用中文?!
2:寫兩遍product節點干什么?手抖么?
哎,我知道自己挺逗的,但是這事要從開發之初說起,我最開始不是用XML來存放數據的,我本來幻想着可以使用CVS文件,或者PRN文件來存放數據源,然而都是以失敗告終,因為商品的描述里什么都有可能存,一些賣萌的編輯什么都會寫的。。。都怪我太年輕,用什么分割數據搞不清啊。。。
好了,回到問題1,我為什么用中文?因為,是為了避免裝13,和讓非技術人員看懂。為什么這么說呢,因為用Excel打開這個模板文件,Excel會把子節點當字段名來顯示,就比如
<產品二維碼地址></產品二維碼地址>
這個節點在Excel里的標題就是“產品二維碼地址”,這就是用中文的原因,方便理解。不過呢,這里提一個有趣的現象,使用中文去取節點里的內容竟然是可以的,很是讓我驚喜哎
代碼里可以看到,我是通過
title = products.product[i].elements()[0]
去取商品名稱的值的,但是實際上
title = products.product[i].產品名稱
也能取得商品名稱的值,感覺帥帥噠!
不過呢,我怕出問題,還是沒有用中文去取。。。
再看問題2,為什么寫兩遍,這個就要問Excel了,因為只寫一個子節點的話,Excel里是沒有表頭的, 寫兩個才有帶表頭的表格,如下
產品名稱 | 里外編碼 | 售價 | 五包費用 | 自提費用 | 規格大小 | 產地 | 材質 | 產品圖片地址 | 產品二維碼地址 |
好了,使用Excel填寫產品信息,再保存為XMl數據文件,准備工作就算完成了,接下來就是見證奇跡的時刻了
怎么運行?在頁面里運行?看着不像啊。。app是個啥?瀏覽器認識么。。
其實是在photoShop里運行的,將寫好的腳本放置於Adobe Photoshop CS5\Presets\Scripts下,打開PS,已經打開的就重啟一下PS,之后你會在PS->文件->腳本下發現放入的腳本文件,默認是支持.jsx結尾的腳本文件,記得文檔里說.js結尾的文件也支持來着。點擊運行之后,你就會發現,圖層啊,文字啊,刷刷的生成,刷刷的關閉,自動化的感覺很好啊,代碼中的圖片生成速度是10s左右一張,最高畫質的,還可以接受。
好了,洋洋灑灑寫了這么多,一是為了記錄一下自己的心得,二是希望能給那些深陷在重復性設計工作中的人們一些幫助,只要稍微懂得一些腳本知識,能看懂英文文檔就可以寫啦,很炫酷的。PS腳本對我來說是一個新鮮的東西,或許我寫的這些小玩意早就有大神在我之前寫過,而且效率更高,或許有現成的軟件可以直接生成,或許我做了很多無用功,但是都沒關系啦,因為有時候是不是坑,你只有跳進去再爬出來才能知道它是不是,這樣以后才好繞着走。博文至此,希望技術大神,和設計大師不用過分重視,畢竟這篇小小的博文只能是一個針對初學者的小例子,就比如說我,我作為一個程序員,兩天前都不知道PS可以運行腳本,而對於那些和我一樣的小伙伴們,如果你們感興趣,自己可以先去寫個腳本,alert一下自己的helloworld!哈哈~