在做Ps插件开发的时候,一个非常常见的功能就是导出图片,有时候我们需要导出整个PSD文档,有时候我们需要导出某个图层或某个几个图层,市面上所有的和UI切图有关的产品和插件,都是运用了插件的图像导出能力来完成这个诉求。本篇文章我们就主要来介绍如何从Ps中获取图像,或许各种格式的图像。
1. 输出文档图片内容
对于导出一整个文档来说,其实是比较简单的,我们查看DOM的API文档就能找到对应的方法
这个方法,就是Ps默认的导出到web格式的实现,导出的格式,其中重点关注最后一个参数ExportOptionsSaveForWeb,通过这个参数的配置,可以输出不同格式、质量的图片,这里给一些例子
1 2 3 4 5 6 7 8
| var pngOption = new ExportOptionsSaveForWeb(); pngOption.format = SaveDocumentType.PNG; pngOption.PNG8 = false; pngOption.quality = 100;
var file = new File("path/to/filename"); app.activeDocument.exportDocument(file, ExportType.SAVEFORWEB, pngOption);
|
同样的,如果要输出其它格式的图片,设置对应的format就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13
| var jpegOption = new ExportOptionsSaveForWeb(); jpegOption.format = SaveDocumentType.JPEG; jpegOption.optimized = true; jpegOption.quality = 100;
var gifOption = new ExportOptionsSaveForWeb(); gifOption.colors = this.config.gifValue; gifOption.PNG8 = true; gifOption.colorReduction = ColorReductionType.SELECTIVE; gifOption.quality = 0; gifOption.dither = Dither.NONE;
|
上面这3种图片格式是比较常见的,PS自身提供的fomat类型非常多,但是一般我们也用不上,这里就不多做介绍了,基本上上面3中类型大体够用了。
2. 输出图层内容
上面的代码,可以将当前打开的整个PSD文档进行图片输出,但是很多时候,我们不要输出整个文档,我们只想导出某个图层的内容,那这个时候该怎么办呢?我们找遍DOM API也找不到可以单独输出图层的方法,Action Manager也没有现成的办法。这种时候,我们可以通过手动操作PS来完成这个事情,然后将这些动作用不同的脚本片段串起来,就可以达到目的了。
整体操作流程如下: 选中需要导出的图层 -> 右键,选择复制图层 -> 在弹出的框里头选择新建文档 -> 该图层会被复制出一个新文档来 -> 菜单栏,图片,裁切,将边缘透明元素裁剪掉,就得到了该图层一个完整的新文档了,这样,我们就可以通过上面的方法将此文档进行导出,流程操作示意图如下:
于是,我们将这几个操作步骤,通过脚本来进行封装,然后串联起来,就实现了导出某个图层的脚本了
由于这些操作都是对Ps进行设置,所以你可以在Ps输出的日志文件(ScriptingListenerJS.log)里头找到对应的Action Manager代码,直接抄作业就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
|
function duplicateLayer() { var desc1 = new ActionDescriptor(); var ref1 = new ActionReference(); ref1.putClass(stringIDToTypeID("document")); desc1.putReference(stringIDToTypeID("null"), ref1); var ref2 = new ActionReference(); ref2.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum")); desc1.putReference(stringIDToTypeID("using"), ref2); desc1.putInteger(stringIDToTypeID("version"), 5); executeAction(stringIDToTypeID("make"), desc1, DialogModes.NO); }
function trimDocument() { var desc1 = new ActionDescriptor(); desc1.putEnumerated(stringIDToTypeID("trimBasedOn"), stringIDToTypeID("trimBasedOn"), stringIDToTypeID("transparency")); desc1.putBoolean(stringIDToTypeID("top"), true); desc1.putBoolean(stringIDToTypeID("bottom"), true); desc1.putBoolean(stringIDToTypeID("left"), true); desc1.putBoolean(stringIDToTypeID("right"), true); executeAction(stringIDToTypeID("trim"), desc1, DialogModes.NO); }
function exportDocument(format, path) { var options = new ExportOptionsSaveForWeb(); if (format == SaveDocumentType.PNG) { options.format = SaveDocumentType.PNG; options.PNG8 = false; options.quality = 100; } else if (format == SaveDocumentType.JPEG) { options.format = SaveDocumentType.JPEG; options.optimized = true; options.quality = this.config.jpegValue; } else if (format == SaveDocumentType.COMPUSERVEGIF) { options.format = SaveDocumentType.COMPUSERVEGIF; options.colors = this.config.gifValue; options.PNG8 = true; options.colorReduction = ColorReductionType.SELECTIVE; options.quality = 0; options.dither = Dither.NONE; }
var file = new File(path); app.activeDocument.exportDocument(file, ExportType.SAVEFORWEB, options); }
|
有了这些分步骤的脚本函数之后,我们就可以将他们串起来,挨个执行一遍,就可以输出想要的图层了,就好比Ps自带的那个动作一样一样滴。
3. 遍历输出多个图层
上面的代码组合,可以帮助我们输出单个图层内容,当我们需要输出多个图层,或者遍历图层输出的时候,需要一些特殊的处理,比如每次新复制出来的图层文档,在导出之后得把它关闭了,重新回到目标文档,以及需要记录目标图层的索引,进行循环遍历。下面以循环输出选中的图层为例子来展示一下,当用户选中多个图层的时候,遍历输出选中的每一个图层。
我在【CEP教程-10】图层处理那些事那篇教程中写了一个Layer类,里面有获取用户选中图层的方法,这里就直接引用了,没有印象的小伙伴自行复习。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
var layers = Layer.getSelectedLayers(); if (layers.length === 0) { alert("You have not selected layer yet!"); return }
for (var i=0; i<layers.length; i++) { var layer = layers[i]; layers.select(); var targetDocument = app.activeDocument; duplicateLayer(); trimDocument(); exportDocument(SaveDocumentType.PNG, "/xx/yy/file.png"); app.activeDocument.close(SaveOptions.DONOTSAVECHANGES); app.activeDocument = targetDocument; }
|
上面这种输出多个图层的方式,是目前市面上绝大多数插件都采用的图层输出方式,稳定可靠也比较方便,对于复制出来的图层文档,你还可以在导出之前做一些别的操作,比如缩放,调整图像效果等等,扩展性很好。我入行的第一款插件切图工具就是基于这个原理实现的。当然,要产品化一个切图工具,里头还有很多细节问题需要处理。
4. 更快的遍历输出
上面的多图层遍历方式,是非常常见和通用的一种办法,但是有一个小缺点:效率不高。
因为每次复制图层到一个新文档的开销是比较大的,当你需要输出很多图层的时候,耗时就会比较长,于是是否有更快一点的办法呢?答案也是有的,但是这里我不打算贴代码了,我介绍一些思路,感兴趣的小伙伴可以按照这个思路自己写一下代码,如果你全程认真看完我的教程的话,相信是可以自己写出来这些代码的。
考虑到每个图层都要复制出一个新文档效率比较低,我们可以这样:
- 将当前文档复制一份出来,后续的操作都在新复制出来的文档中进行,不影响原稿
- 选中复制出来的文档,隐藏掉所有的图层
- 选中并显示第一个要导出的图层
- 裁切文档的透明元素
- 导出当前文档
- 回退历史步骤到裁切文档那一步
- 隐藏当前图层
- 选中并显示下一个要导出的图层,回到步骤4
这个方法,由于只复制一次新文档,后续的步骤都在此文档上进行,就会省去很多开销,速度会快一些,流程里头多了一步历史步骤的回退操作,这个在DOM API里头有对应的方法,相信大家也都能学会。
5. 输出SVG/PDF格式图片
上面我们提到的输出图片格式包含了PNG/JPEG/GIF等常见图片格式,有时候,我们会需要用到SVG/PDF这样的图片格式,他们的输出方式和前面有一些不太一样,单独辟出来介绍。
SVG是一种矢量图形格式,所以要输出它,对原始图形有要求,并不是所有内容都可以成功输出SVG的,你的图层最好是形状类型,这样才能正常输出SVG,PS没有提供相关的API和方法让我们完成这个功能,但是PS其实自带了这个特性,选中图层,鼠标右键,可以看到一个拷贝SVG的选项
这就是PS提供给我们的功能,并且,它其实是官方自己写好的JSX脚本文件,本质上点这个选项,就是执行该JSX文件,这个文件我们可以在如下地址找到
{PS安装路径}/Required/CopySVGToClipboard.jsx
这个脚本给我们提供了一个生成当前图层SVG文本的能力,我们可以直接借用此能力来进行SVG输出。我们不需要把这个文件的代码抄过来(代码非常多),我们可以直接引用这个文件就可以了,我在前面的文章中介绍了,JSX运行时给我们提供了一个**$.eval**函数,可以引用执行独立的JSX脚本文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function getAppPath() { var kexecutablePathStr = stringIDToTypeID("executablePath"); var desc = new ActionDescriptor(); var ref = new ActionReference(); ref.putProperty(charIDToTypeID('Prpr'), kexecutablePathStr); ref.putEnumerated(charIDToTypeID('capp'), charIDToTypeID('Ordn'), charIDToTypeID('Trgt')); desc.putReference(charIDToTypeID('null'), ref); var result = executeAction(charIDToTypeID('getd'), desc, DialogModes.NO); return File.decode(result.getPath(kexecutablePathStr)); }
function exportSVG(filename) { var appFolder = { Windows: "/", Macintosh: "/../" }; var svgFile = new File(getAppPath() + appFolder[File.fs] + "Required/CopySVGToClipboard.jsx"); $.evalFile(svgFile.absoluteURI); var params = { layerId: app.activeDocument.activeLayer.id, layerScale: 1, documentId: app.activeDocument.id }; var svgText = svg.createSVGText();
var file = new File(filename); file.open("w"); file.encoding = "UTF-8"; file.lineFeed = "Unix"; file.write(svgText); file.close(); }
|
导出PDF则要更简单一些,PS默认的另存为那个框,就有PDF选项,我们自己实际操作一下,然后从日志输出里头抄代码就可以了,但是它是针对文档的,所以如果你是图层,也记得单独弄成文档的形态再进行操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| function exportPDF(path) { var idsave = charIDToTypeID( "save" ); var desc322 = new ActionDescriptor(); var idAs = charIDToTypeID( "As " ); var desc323 = new ActionDescriptor(); let idpdfCompatibilityLevel = stringIDToTypeID( "pdfCompatibilityLevel" ); desc323.putEnumerated( idpdfCompatibilityLevel, idpdfCompatibilityLevel, stringIDToTypeID( "pdf15" )); desc323.putBoolean( stringIDToTypeID( "pdfPreserveEditing" ), false ); desc323.putInteger( stringIDToTypeID( "pdfCompressionType" ), 7 ); desc323.putBoolean( stringIDToTypeID( "pdfIncludeProfile" ), false ); desc322.putObject( idAs, charIDToTypeID( "PhtP" ), desc323 ); desc322.putPath( charIDToTypeID( "In " ), new File( path ) ); desc322.putInteger( charIDToTypeID( "DocI" ), activeDocument.id ); desc322.putBoolean( charIDToTypeID( "Cpy " ), true ); desc322.putBoolean( charIDToTypeID( "Lyrs" ), false ); var idsaveStage = stringIDToTypeID( "saveStage" ); var idsaveStageType = stringIDToTypeID( "saveStageType" ); var idsaveSucceeded = stringIDToTypeID( "saveSucceeded" ); desc322.putEnumerated( idsaveStage, idsaveStageType, idsaveSucceeded ); executeAction( idsave, desc322, DialogModes.NO ); }
|
6. 后台导出图片
前文介绍的这些图片输出的方法,本质上,都是在模拟用户的真实操作,就好比录制了一系列动作一般,所以,这些方法在输出图片的时候,都会阻塞UI进程,用户能够看到当前图片的各种操作行为,新建一个文档,关闭一个文档等等。这就带来一个问题,当我们需要输出很多图片的时候,用户的Ps就堵在那里了,必须等所有的任务都执行结束之后才能进行操作,Ps这个期间就跟卡死一样,体验总体来说不太好。
那有没有能够在后台默默导出图片的方法呢?
答案是有的,就是生成器,生成器相关的知识,我在上一篇文章已经详细的介绍了,忘了的小朋友可以翻回去复习一下,用生成器来输出图片,可以在子线程中进行,不会影响用户的操作,可以做到无感,在某些特定场景下是非常有价值的。
7. 其它图片输出方式
除了前文提到的document.export方法之外,其实还有一些其它的API可以进行文件保存操作
A) Action Manager命令
AM提供了一个export命令,也可以进行图片导出,并且支持很多参数的配置,总体对标的是Ps原生的这个功能
该命令的具体参数,大家可以在ScriptListenerJS.log里头找到,我就不再贴代码了
B) saveAs方法
DOM API里头还提供了一个saveAs方法,也可以进行PNG/GIF/JPEG格式的输出
1 2 3 4
| var saveFile = new File("path/to/filename.png"); var pngSaveOptions = new PNGSaveOptions(); pngSaveOptions.interlaced = exportInfo.pngInterlaced; app.activeDocument.saveAs(saveFile, pngSaveOptions, true, Extension.LOWERCASE);
|
上面这些都是JSX提供的内容,除此之外,也可以通过C++的插件方式进行图片导出,这块我目前还没有研究,后续调研之后再写文章给大家分享
总结
本篇文章介绍了在Ps中输出图片的各种办法,图片导出是一个很常见的功能,希望对大家有帮助。
下一篇文章,我们来介绍Nodejs在CEP插件开发中的应用,敬请期待~~