開博第二篇:記一個利用JavaScript,編寫PS腳本,開發圖片量產工具


背景:身在一個有實業的電商公司,設計部的妹子們總是會有做不完的商品圖片,當然了,要是做點有技術含量的美化工作也罷,但是最近她們很是無聊,總是要做一些重復性的工作,就比如如題所說的,圖片量產,量產什么呢?價格牌。。。這東西很沒意思哎!就是給你一個模板,然后你自己把模板原來的文字圖片換掉就行了,再排一下版,純體力勞動好么!博主做過一陣子的對日外包工作,深知她們的痛苦,如果說某些對日外包的程序猿是人肉轉碼器的話,那么設計部的妹子們現在就成了。。算了,詞就不說了,太殘酷了

========================================炫炸天的分割線========================================

線索:針對背景交代的情況,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!哈哈~

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM