├── Export Layers To Files (Fast)-progress_bar.json ├── LICENSE ├── Export Layers To Files (Fast)-dialog.json ├── README.md └── Export Layers To Files (Fast).jsx /Export Layers To Files (Fast)-progress_bar.json: -------------------------------------------------------------------------------- 1 | palette { 2 | text: "Please wait...", 3 | preferredSize: [350, 60], 4 | orientation: "column", 5 | alignChildren: "fill", 6 | barRow: Group { 7 | orientation: "row", 8 | bar: Progressbar { 9 | preferredSize: [300, 16] 10 | }, 11 | cancelBtn: Button { 12 | text: "Cancel" 13 | } 14 | }, 15 | lblMessage: StaticText { 16 | alignment: "left", 17 | text: "" 18 | }, 19 | warning: Panel { 20 | orientation: "column", 21 | alignChildren: "fill", 22 | message: StaticText { 23 | text: "Don't make changes to the current document while the script is running!", 24 | properties: { 25 | multiline: true 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 - 2014 Simon Kruchinin, Johannes Walter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Export Layers To Files (Fast)-dialog.json: -------------------------------------------------------------------------------- 1 | dialog { 2 | text: "Export Layers To Files", 3 | orientation: "column", 4 | alignChildren: "fill", 5 | lblDest: StaticText { 6 | alignment: "left", 7 | text: "Destination:" 8 | }, 9 | funcArea: Group { 10 | orientation: "row", 11 | alignChildren: "top", 12 | content: Group { 13 | orientation: "column", 14 | alignChildren: "left", 15 | grpDest: Group { 16 | orientation: "row", 17 | alignChildren: "left", 18 | txtDest: EditText {preferredSize: [310, 20], properties: {readonly: false}}, 19 | btnDest: Button {text: "Browse..."} 20 | }, 21 | grpLayers: Group { 22 | orientation: "row", 23 | txtLayers: StaticText {text: "Export:", preferredSize: [80, 20]}, 24 | radioLayersAll: RadioButton {text: "All layers", preferredSize: [80, 20], value: true}, 25 | radioLayersVis: RadioButton {text: "Visible only", preferredSize: [100, 20]}, 26 | radioLayersSel: RadioButton {text: "Selected group", preferredSize: [120, 20]}, 27 | cbIgnoreLayers: Checkbox {text: "Ignore layers starting with :"}, 28 | EditIgnoreLayers : EditText {preferredSize: [50, 20]} 29 | }, 30 | grpNaming: Group { 31 | orientation: "row", 32 | txtNaming: StaticText {text: "Name files:", preferredSize: [80, 20]}, 33 | drdNaming: DropDownList {preferredSize: [140, 20], properties: {items: ["As layers, strip extension", "As layers", "By index from top", "By index from bottom"]}}, 34 | cbNaming: Checkbox {text: "Allow spaces"}, 35 | radioUnderscore: RadioButton {text: "Use Underscore", preferredSize: [120, 20], value: true}, 36 | radioHyphen: RadioButton {text: "Use Hyphen", preferredSize: [120, 20]}, 37 | }, 38 | grpLetterCase: Group { 39 | orientation: "row", 40 | txtLetterCase: StaticText {text: "Letter case:", preferredSize: [80, 20]}, 41 | drdLetterCase: DropDownList {preferredSize: [140, 20], properties: {items: ["Keep", "Lowercase", "Uppercase"]}} 42 | }, 43 | grpPrefix: Group { 44 | orientation: "row", 45 | alignChildren: "left", 46 | txtPrefix: StaticText {text: "Prefix/suffix:", preferredSize: [80, 20], helpTip: "Prefix and/or suffix that will be added to the exported file names. A space is added automatically after/before."}, 47 | editPrefix: EditText {preferredSize: [140, 20]}, 48 | txtSep: StaticText {text: "- ... -"}, 49 | editSuffix: EditText {preferredSize: [140, 20]} 50 | }, 51 | grpFolderTree: Group { 52 | orientation: "row", 53 | alignChildren: "left", 54 | cbFolderTree: Checkbox {text: "Groups as folders", helpTip: "Exports groups as a folder tree."}, 55 | blankLayers: StaticText {text: " "}, 56 | cbTopGroupAsFolder: Checkbox {text: "Top Groups as folders", helpTip: "Let All top groups be sub folders."}, 57 | blankLayers: StaticText {text: " "}, 58 | cbTopGroupAsLayer: Checkbox {text: "Top Groups as layers", helpTip: "Merge Top Groups first."}, 59 | }, 60 | grpTrim: Group { 61 | orientation: "row", 62 | txtTrim: StaticText {text: "Trim:", preferredSize: [80, 20]}, 63 | drdTrim: DropDownList {preferredSize: [140, 20], properties: {items: ["No", "Each layer", "Combined"]}}, 64 | cbTrim: Checkbox {text: "Use trim()", helpTip: "Won't trim if a background layer is on, but may behave better on Mac."} 65 | }, 66 | grpScale: Group { 67 | orientation: "row", 68 | txtScale: StaticText {text: "Scale:", preferredSize: [80, 20]}, 69 | cbScale: Checkbox {text: "Scale", helpTip: "Scale layer by x percents"}, 70 | editScale: EditText {preferredSize: [50, 20], properties: {readonly: false}}, 71 | txtScalePercent: StaticText {text: "% ", preferredSize: [20, 20]}, 72 | }, 73 | grpPadding: Group { 74 | orientation: "row", 75 | txtPadding: StaticText {text: "Padding:", preferredSize: [80, 20]}, 76 | cbPadding: Checkbox {text: "Add padding", helpTip: "Add x pixels of transparency"}, 77 | editPadding: EditText {preferredSize: [50, 20], properties: {readonly: false}}, 78 | txtPaddingPx: StaticText {text: "px", preferredSize: [20, 20]}, 79 | }, 80 | grpBgFgLayer: Group { 81 | cbBgLayer: Checkbox {text: "Lowest layer is background", helpTip: "Leaves the bottommost layer on while exporting every other layer one by one. It will act as background."}, 82 | blankLayers: StaticText {text: " "}, 83 | cbFgLayer: Checkbox {text: "Highest layer is foreground", helpTip: "Leaves the topmost layer on while exporting every other layer one by one. It will act as foreground"}, 84 | }, 85 | grpFileType: Group { 86 | orientation: "row", 87 | txtFileType: StaticText {text: "File type:", preferredSize: [80, 20]}, 88 | drdFileType: DropDownList {preferredSize: [70, 20]} 89 | }, 90 | pnlOptions: Panel { 91 | text: "Options:", 92 | orientation: "stack", 93 | alignment: "fill", 94 | preferredSize: [200, 100] 95 | } 96 | }, 97 | buttons: Group { 98 | orientation: "column", 99 | btnRun: Button {text: "Run"}, 100 | btnCancel: Button {text: "Cancel"}, 101 | txtSpace: StaticText {}, 102 | btnSettings: Button {text: "Save and Cancel", helpTip: "Save current settings and cancel."}, 103 | txtSpace: StaticText {}, 104 | cbOverwrite: Checkbox {text: "Overwrite files", helpTip: "Overwrite existing files"} 105 | } 106 | }, 107 | warning: Panel { 108 | alignChildren: "fill", 109 | message: StaticText { 110 | text: "This document contains {0} layer(s), {1} of them visible, {2} selected.\n\nProcessing a large number of layers may take some time!\n\n(If you check \"Allow spaces\", make sure they are not replaced by Photoshop itself in \"Save For Web\" settings.)", 111 | properties: { 112 | multiline: true 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Photoshop-Export-Layers-to-Files-Fast 2 | ================================= 3 | 4 | This script allows you to export layers in your Photoshop document as individual files at a speed much faster than the built-in script from Adobe. So far it does not feature all the formats that the built-in version does, but more can be added easily upon request. Feel free to contribute to it and make it even more powerful! 5 | 6 | Please, consider donating a modest sum if you enjoy using our script regularly and would like to see more features implemented sooner. 7 | 8 | This script was originally built as a response to a [question](http://graphicdesign.stackexchange.com/questions/1961/export-photoshop-layers-to-individual-png-files-batch-process) on [graphicdesign.stackexchange.com](http://graphicdesign.stackexchange.com/). 9 | 10 | Make sure to download all files: 11 | 12 | [Download as zip](https://github.com/jwa107/Photoshop-Export-Layers-to-Files-Fast/archive/master.zip) 13 | 14 | Disclaimer: We are not associated with Adobe in any way. For any issues relating to Adobe products or Adobe scripts please contact them directly. We have never had an issue, but please use this script at your own risk. We are not responsible for any lost data or damaged PSDs so always make a back-up. 15 | 16 | Features: 17 | ------------------------------- 18 | * Supported export formats: 19 | * PNG (8 and 24 bit) 20 | * JPEG 21 | * Targa 22 | * BMP 23 | * Handles nesting in grouped layers. 24 | * Export either all layers or visible only. 25 | * Shows current progress and allows to cancel any time. 26 | * Shows proper layer count in advance. 27 | * Files are named either using layer names or automatic layer indices. 28 | * Lowest layer can be treated as common background. 29 | * Exported images can have layer size or canvas size (trimming option). 30 | * Last used dialog settings are remembered. 31 | * A selected group can be exported as usual (layer by layer) while everything else is left in tact. (This way variable content can be exported for complex fixed background and foreground.) 32 | * Can export groups as folder hierarchy. Conflicting folders are renamed. 33 | 34 | You can also see [what's coming next](https://github.com/jwa107/Photoshop-Export-Layers-to-Files-Fast/wiki/Feature-Roadmap) and browse some of our [performance test results](https://github.com/jwa107/Photoshop-Export-Layers-to-Files-Fast/wiki/Performance-Test-Results). 35 | 36 | Requirements: 37 | ------------------------------- 38 | Adobe Photoshop CS2 or higher. 39 | 40 | How To Use: 41 | ------------------------------- 42 | 1. Open Photoshop 43 | 2. File -> Scripts -> Browse... 44 | 3. Locate the file, and open it. 45 | 46 | Alternatively move the script into the /presets/scripts directory, located by in your Photoshop folder. 47 | 48 | Windows: /Program Files/Adobe/Adobe Photoshop VERSION/Presets/Scripts/ 49 | 50 | Mac: /Applications/Adobe Photoshop VERSION/Presets/Scripts/ 51 | 52 | 53 | Version History: 54 | ------------------------------- 55 | 17 Sept 2018 by [willena](https://github.com/Willena), [finscn](https://github.com/finscn), [jgod](https://github.com/jgod) 56 | 57 | * Ability to ignore layer starting with an "exclude string" (implemented by @jgod, improved by me; Related subbranch : https://github.com/Willena/Photoshop-Export-Layers-to-Files-Fast/tree/jgod-ignore-layers-starting-with-bang) 58 | * Similar to the background feature, it is now possible to keep the highest layer on top of all other layer when exporting. (Related issues : #108, #96, #42; Related subbranch : https://github.com/Willena/Photoshop-Export-Layers-to-Files-Fast/tree/first-layer-as-foreground) 59 | * Groups can now be exported as a single image/layer. (implemented by @finscn, updated to be mergeable by me) ( related issues : #79 #70 #69 #21 #108 ; Related subbranch : https://github.com/Willena/Photoshop-Export-Layers-to-Files-Fast/tree/finscn-groups-to-layers) 60 | * Add Transparent padding pixels before export (Related issue : #64 ; Related subbranch : https://github.com/Willena/Photoshop-Export-Layers-to-Files-Fast/tree/transparent-padding-pixels) 61 | * Scale image before export (Related subbranch : https://github.com/Willena/Photoshop-Export-Layers-to-Files-Fast/tree/scale-before-export) 62 | 63 | 64 | 29 April 2016 by [Skjorn](https://github.com/skjorn) 65 | 66 | * The option "Groups as folders" exports layers in a folder tree (same as groups) instead of a flat list. 67 | 68 | 05 May 2015 by [Skjorn](https://github.com/skjorn) 69 | 70 | * A selected group can be exported as usual (layer by layer) while everything else is left in tact. (This way variable content can be exported for complex fixed background and foreground.) _Works only for the first selected group!_ If no layer/group is selected, the topmost one is assumed by Photoshop. All parent groups of the selection are made visible. Visibility of exported layers is not taken into account; all are exported. 71 | 72 | 25 April 2015 by [Johannes Walter](https://github.com/jwa107) and [Skjorn](https://github.com/skjorn) 73 | 74 | * Last user settings remembered for versions CS 3 and higher. 75 | 76 | 21 November 2014 by [Skjorn](https://github.com/skjorn) 77 | 78 | * Added option to strip extensions from layer names and made default. 79 | 80 | 26 September 2014 by [Skjorn](https://github.com/skjorn) 81 | 82 | * Added layer trimming. 83 | 84 | 01 August 2014 by [Skjorn](https://github.com/skjorn) 85 | 86 | * Lowest layer can be treated as common background. 87 | 88 | 10 July 2014 by [Skjorn](https://github.com/skjorn) 89 | 90 | * Added BMP support. 91 | * Enabled using layer indices for file names. 92 | * Optional file name prefix. 93 | 94 | 23 June 2014 by [Skjorn](https://github.com/skjorn) 95 | 96 | * Added progress bars with cancel buttons. 97 | * Slow layer retrieval sped up and moved at the beginning. 98 | * Dialog shows the correct layer count. 99 | 100 | 08 June 2014 by [Skjorn](https://github.com/skjorn) 101 | 102 | * Script renamed to match the built-in version. 103 | * The dialog extended. 104 | * Destination folder made selectable. 105 | * Added an option to export only visible layers and fixed visibility toggling after export. 106 | * The script made faster! 107 | * The document doesn't have to be saved and be a layered PSD anymore. Exporting from a temporary working copy is fine. 108 | * Major rewrite of internals. 109 | 110 | 22 May 2014 by [Skjorn](https://github.com/skjorn) 111 | 112 | * Added support for Targa (TGA). 113 | 114 | 02 December 2013 by [Justin Wang](http://www.github.com/Tangleworm) 115 | 116 | * Added support for both PNG-24 and PNG-8 117 | 118 | 24 May 2013 by [Johannes Walter](https://github.com/jwa107) 119 | 120 | * Nesting properly handled 121 | * All layers save seperately again (no more stacking). 122 | 123 | 27 March 2013 by [Robin Parmar](http://robinparmar.com/) (robin(at)robinparmar(dot)com) 124 | 125 | * preferences stored in object 126 | * auto-increment file names to prevent collisions 127 | * properly handles layer groups 128 | * header added 129 | * code comments added 130 | * main() now has error catcher 131 | * counts number of layers 132 | * many little code improvements 133 | 134 | 26 Sept 2012 by [Johannes Walter](https://github.com/jwa107) 135 | 136 | * Original version 137 | 138 | 139 | Contact: 140 | ------------------------------- 141 | Please use communication channels on GitHub to write feedback/bugs/suggestions: https://github.com/jwa107/Photoshop-Export-Layers-to-Files-Fast/issues 142 | -------------------------------------------------------------------------------- /Export Layers To Files (Fast).jsx: -------------------------------------------------------------------------------- 1 | // NAME: 2 | // Export Layers To Files 3 | 4 | // DESCRIPTION: 5 | // Improved version of the built-in "Export Layers To Files" script: 6 | // * Supports PNG and possibly other formats in the future. 7 | // * Does not create multiple document duplicates, so it's much faster. 8 | // Saves each layer in the active document to a file in a preferred format named after the layer. Supported formats: 9 | // * PNG 10 | // * JPEG 11 | // * Targa 12 | // * BMP 13 | 14 | // REQUIRES: 15 | // Adobe Photoshop CS2 or higher 16 | 17 | // Most current version always available at: https://github.com/jwa107/Photoshop-Export-Layers-as-Images 18 | 19 | // enable double-clicking from Finder/Explorer (CS2 and higher) 20 | #target photoshop 21 | 22 | app.bringToFront(); 23 | 24 | // 25 | // Type definitions 26 | // 27 | 28 | var FileNameType = { 29 | AS_LAYERS: 1, 30 | INDEX_ASC: 2, 31 | INDEX_DESC: 3, 32 | AS_LAYERS_NO_EXT: 4, 33 | 34 | values: function() { 35 | return [this.AS_LAYERS_NO_EXT, this.AS_LAYERS, this.INDEX_DESC, this.INDEX_ASC]; 36 | }, 37 | 38 | forIndex: function(index) { 39 | return this.values()[index]; 40 | }, 41 | 42 | getIndex: function(value) { 43 | return indexOf(this.values(), value); 44 | } 45 | }; 46 | 47 | var LetterCase = { 48 | KEEP: 1, 49 | LOWERCASE: 2, 50 | UPPERCASE: 3, 51 | 52 | values: function() { 53 | return [this.KEEP, this.LOWERCASE, this.UPPERCASE]; 54 | }, 55 | 56 | forIndex: function(index) { 57 | return this.values()[index]; 58 | }, 59 | 60 | getIndex: function(value) { 61 | return indexOf(this.values(), value); 62 | }, 63 | 64 | toExtensionType: function(value) { 65 | switch (value) { 66 | 67 | case this.KEEP: 68 | return Extension.NONE; 69 | 70 | case this.LOWERCASE: 71 | return Extension.LOWERCASE; 72 | 73 | case this.UPPERCASE: 74 | return Extension.UPPERCASE; 75 | 76 | default: 77 | return Extension.NONE; 78 | } 79 | } 80 | }; 81 | 82 | var TrimPrefType = { 83 | DONT_TRIM: 1, 84 | INDIVIDUAL: 2, 85 | COMBINED: 3, 86 | 87 | values: function() { 88 | return [this.DONT_TRIM, this.INDIVIDUAL, this.COMBINED]; 89 | }, 90 | 91 | forIndex: function(index) { 92 | return this.values()[index]; 93 | }, 94 | 95 | getIndex: function(value) { 96 | return indexOf(this.values(), value); 97 | } 98 | }; 99 | 100 | var ExportLayerTarget = { 101 | ALL_LAYERS: 1, 102 | VISIBLE_LAYERS: 2, 103 | SELECTED_LAYERS: 3, // Export selection, leave the rest as is, visibility for parent groups will be forced. 104 | 105 | values: function() { 106 | return [this.ALL_LAYERS, this.VISIBLE_LAYERS, this.SELECTED_LAYERS]; 107 | }, 108 | 109 | forIndex: function(index) { 110 | return this.values()[index]; 111 | }, 112 | 113 | getIndex: function(value) { 114 | return indexOf(this.values(), value); 115 | } 116 | }; 117 | 118 | // Settings 119 | 120 | var USER_SETTINGS_ID = "exportLayersToFilesCustomDefaultSettings"; 121 | var DEFAULT_SETTINGS = { 122 | // common 123 | topGroupAsLayer: app.stringIDToTypeID("topGroupAsLayer"), 124 | topGroupAsFolder: app.stringIDToTypeID("topGroupAsFolder"), 125 | destination: app.stringIDToTypeID("destFolder"), 126 | overwrite: app.stringIDToTypeID("overwrite"), 127 | exportLayerTarget: app.stringIDToTypeID("exportLayerTarget"), 128 | nameFiles: app.stringIDToTypeID("nameFiles"), 129 | allowSpaces: app.stringIDToTypeID("allowSpaces"), 130 | letterCase: app.stringIDToTypeID("letterCase"), 131 | outputPrefix: app.stringIDToTypeID("outputPrefix"), 132 | outputSuffix: app.stringIDToTypeID("outputSuffix"), 133 | trim: app.stringIDToTypeID("trim"), 134 | scale: app.stringIDToTypeID("scale"), 135 | scaleValue: app.stringIDToTypeID("scaleValue"), 136 | exportBackground: app.stringIDToTypeID("exportBackground"), 137 | exportForeground: app.stringIDToTypeID("exportForeground"), 138 | fileType: app.stringIDToTypeID("fileType"), 139 | forceTrimMethod: app.stringIDToTypeID("forceTrimMethod"), 140 | groupsAsFolders: app.stringIDToTypeID("groupsAsFolders"), 141 | ignoreLayersString: app.stringIDToTypeID('ignoreLayersString'), 142 | ignoreLayers: app.stringIDToTypeID('ignoreLayers'), 143 | padding: app.stringIDToTypeID("padding"), 144 | paddingValue: app.stringIDToTypeID("paddingValue") 145 | }; 146 | 147 | // 148 | // Global variables 149 | // 150 | 151 | var env = new Object(); 152 | env.profiling = false; 153 | 154 | var prefs = new Object(); 155 | var userCancelled = false; 156 | var layers; 157 | var visibleLayers; 158 | var selectedLayers; 159 | var groups; 160 | var layerCount = 0; 161 | var visibleLayerCount = 0; 162 | var selectedLayerCount = 0; 163 | 164 | 165 | // 166 | // Entry point 167 | // 168 | 169 | bootstrap(); 170 | 171 | // 172 | // Processing logic 173 | // 174 | 175 | function main() { 176 | // user preferences 177 | prefs = new Object(); 178 | prefs.format = ""; 179 | prefs.fileExtension = ""; 180 | try { 181 | prefs.filePath = app.activeDocument.path; 182 | } catch (e) { 183 | prefs.filePath = Folder.myDocuments; 184 | } 185 | prefs.formatArgs = null; 186 | prefs.exportLayerTarget = ExportLayerTarget.ALL_LAYERS; 187 | prefs.outputPrefix = ""; 188 | prefs.outputSuffix = ""; 189 | prefs.naming = FileNameType.AS_LAYERS_NO_EXT; 190 | prefs.namingLetterCase = LetterCase.KEEP; 191 | prefs.replaceSpaces = true; 192 | prefs.delimiter = '_'; 193 | prefs.bgLayer = false; 194 | prefs.fgLayer = false; 195 | prefs.trim = TrimPrefType.DONT_TRIM; 196 | prefs.scale = false; 197 | prefs.scaleValue = 100; 198 | prefs.forceTrimMethod = false; 199 | prefs.groupsAsFolders = true; 200 | prefs.overwrite = false; 201 | prefs.padding = false; 202 | prefs.paddingValue = 1; 203 | prefs.ignoreLayersString = "!"; 204 | prefs.ignoreLayers = false; 205 | 206 | userCancelled = false; 207 | 208 | // create progress bar 209 | var progressBarWindow = createProgressBar(); 210 | if (!progressBarWindow) { 211 | return "cancel"; 212 | } 213 | 214 | // count layers 215 | var profiler = new Profiler(env.profiling); 216 | var layerCountResult = countLayers(progressBarWindow); 217 | if (userCancelled) { 218 | return "cancel"; 219 | } 220 | layerCount = layerCountResult.layerCount; 221 | visibleLayerCount = layerCountResult.visibleLayerCount; 222 | selectedLayerCount = layerCountResult.selectedLayerCount; 223 | var countDuration = profiler.getDuration(true, true); 224 | if (env.profiling) { 225 | alert("Layers counted in " + profiler.format(countDuration), "Debug info"); 226 | } 227 | 228 | // show dialogue 229 | if (showDialog() === 1) { 230 | env.documentCopy = app.activeDocument.duplicate(); 231 | 232 | // collect layers 233 | profiler.resetLastTime(); 234 | if (prefs.topGroupAsLayer) { 235 | mergeTopGroups(app.activeDocument); 236 | } 237 | var collected = collectLayers(progressBarWindow); 238 | if (userCancelled) { 239 | alert("Export cancelled! No files saved.", "Finished", false); 240 | return "cancel"; 241 | } 242 | layers = collected.layers; 243 | visibleLayers = collected.visibleLayers; 244 | selectedLayers = collected.selectedLayers; 245 | groups = collected.groups; 246 | var collectionDuration = profiler.getDuration(true, true); 247 | if (env.profiling) { 248 | alert("Layers collected in " + profiler.format(collectionDuration), "Debug info"); 249 | } 250 | 251 | // create unique folders 252 | 253 | var foldersOk = !prefs.groupsAsFolders; 254 | if (prefs.groupsAsFolders) { 255 | foldersOk = createUniqueFolders(prefs.exportLayerTarget); 256 | if (foldersOk !== true) { 257 | alert(foldersOk + " Not exporting layers.", "Failed", true); 258 | } 259 | } 260 | 261 | // export 262 | if (foldersOk === true) { 263 | profiler.resetLastTime(); 264 | 265 | var count = exportLayers(prefs.exportLayerTarget, progressBarWindow); 266 | var exportDuration = profiler.getDuration(true, true); 267 | 268 | var message = ""; 269 | if (userCancelled) { 270 | message += "Export cancelled!\n\n"; 271 | } 272 | message += "Saved " + count.count + " files."; 273 | if (env.profiling) { 274 | message += "\n\nExport function took " + profiler.format(collectionDuration) + " + " + profiler.format(exportDuration) + " to perform."; 275 | } 276 | if (count.error) { 277 | message += "\n\nSome layers failed to export! (Are there many layers with the same name?)"; 278 | } 279 | alert(message, "Finished", count.error); 280 | } 281 | 282 | app.activeDocument.close(SaveOptions.DONOTSAVECHANGES); 283 | env.documentCopy = null; 284 | } else { 285 | return "cancel"; 286 | } 287 | } 288 | 289 | function exportLayers(exportLayerTarget, progressBarWindow) { 290 | var retVal = { 291 | count: 0, 292 | error: false 293 | }; 294 | var doc = app.activeDocument; 295 | 296 | // Select a subset of layers to export. 297 | 298 | var layerCount = layers.length; 299 | var layersToExport; 300 | 301 | 302 | switch (exportLayerTarget) { 303 | case ExportLayerTarget.ALL_LAYERS: 304 | layersToExport = layers; 305 | break; 306 | 307 | case ExportLayerTarget.VISIBLE_LAYERS: 308 | layersToExport = visibleLayers; 309 | break; 310 | 311 | case ExportLayerTarget.SELECTED_LAYERS: 312 | layersToExport = selectedLayers; 313 | // Bg layer is redundant since everything else outside the selection is essentially a background/foreground. 314 | prefs.bgLayer = false; 315 | prefs.fgLayer = false; 316 | break; 317 | 318 | default: 319 | layersToExport = layers; 320 | break; 321 | } 322 | 323 | var count = prefs.bgLayer ? layersToExport.length - 1 : layersToExport.length; 324 | 325 | if (count < 1) { 326 | return retVal; 327 | } 328 | 329 | // Export. 330 | 331 | if ((layerCount == 1) && (layers[0].layer.isBackgroundLayer || prefs.fgLayer)) { 332 | // Flattened images don't support LayerComps or visibility toggling, so export it directly. 333 | 334 | storeHistory(); 335 | 336 | if (prefs.scale) 337 | scaleImage(); 338 | 339 | if (prefs.padding) 340 | addPadding(); 341 | 342 | if (saveImage(layers[0].layer.name)) { 343 | ++retVal.count; 344 | } else { 345 | retVal.error = true; 346 | } 347 | 348 | restoreHistory(); 349 | 350 | } else { 351 | // Single trim of all layers combined. 352 | if (prefs.trim == TrimPrefType.COMBINED) { 353 | var UPDATE_NUM = 20; 354 | if (progressBarWindow) { 355 | var stepCount = (exportLayerTarget == ExportLayerTarget.ALL_LAYERS) ? count / UPDATE_NUM + 1 : 1; 356 | showProgressBar(progressBarWindow, "Trimming...", stepCount); 357 | } 358 | 359 | if (exportLayerTarget == ExportLayerTarget.ALL_LAYERS) { 360 | // For combined trim across all layers, make all layers visible. 361 | for (var i = 0; i < count; ++i) { 362 | makeVisible(layersToExport[i]); 363 | 364 | if (progressBarWindow && (i % UPDATE_NUM == 0)) { 365 | updateProgressBar(progressBarWindow); 366 | repaintProgressBar(progressBarWindow); 367 | if (userCancelled) { 368 | progressBarWindow.hide(); 369 | return retVal; 370 | } 371 | } 372 | } 373 | } 374 | 375 | doc.trim(TrimType.TRANSPARENT); 376 | } 377 | 378 | if (progressBarWindow) { 379 | showProgressBar(progressBarWindow, "Exporting 1 of " + count + "...", count); 380 | } 381 | 382 | // Turn off all layers when exporting all layers - even seemingly invisible ones. 383 | // When visibility is switched, the parent group becomes visible and a previously invisible child may become visible by accident. 384 | for (var i = 0; i < count; ++i) { 385 | makeInvisible(layersToExport[i]); 386 | //layersToExport[i].layer.visible = false; 387 | } 388 | if (prefs.bgLayer) { 389 | makeVisible(layersToExport[count]); 390 | } 391 | 392 | if (prefs.fgLayer) { 393 | makeVisible(layersToExport[0]); 394 | } 395 | 396 | var countDigits = 0; 397 | if (prefs.naming != FileNameType.AS_LAYERS) { 398 | countDigits = ("" + count).length; 399 | } 400 | 401 | // export layers 402 | for (var i = (prefs.fgLayer ? 1 : 0); i < count; ++i) { 403 | var layer = layersToExport[i].layer; 404 | 405 | // Ignore layers that have a bang in front, ie: "not". 406 | if (layer.name.indexOf(prefs.ignoreLayersString) === 0) continue; 407 | 408 | var fileName; 409 | switch (prefs.naming) { 410 | 411 | case FileNameType.AS_LAYERS_NO_EXT: 412 | fileName = makeFileNameFromLayerName(layersToExport[i], true); 413 | break; 414 | 415 | case FileNameType.AS_LAYERS: 416 | fileName = makeFileNameFromLayerName(layersToExport[i], false); 417 | break; 418 | 419 | case FileNameType.INDEX_ASC: 420 | fileName = makeFileNameFromIndex(count - i, countDigits, layersToExport[i]); 421 | break; 422 | 423 | case FileNameType.INDEX_DESC: 424 | fileName = makeFileNameFromIndex(i + 1, countDigits, layersToExport[i]); 425 | break; 426 | } 427 | 428 | if (fileName) { 429 | if ((prefs.trim != TrimPrefType.INDIVIDUAL) || ((layer.bounds[0] < layer.bounds[2]) && ((layer.bounds[1] < layer.bounds[3])))) { // skip empty layers when trimming 430 | 431 | storeHistory(); 432 | 433 | makeVisible(layersToExport[i]); 434 | 435 | if (prefs.trim == TrimPrefType.INDIVIDUAL) { 436 | var useTrim = prefs.forceTrimMethod; 437 | 438 | if (!useTrim) { 439 | try { 440 | doc.crop(layer.bounds); 441 | } catch (e) { 442 | useTrim = true; 443 | } 444 | } 445 | 446 | if (useTrim) { 447 | doc.trim(TrimType.TRANSPARENT); 448 | } 449 | } 450 | 451 | var folderSafe = true; 452 | if (prefs.groupsAsFolders) { 453 | var parentFolder = (new File(fileName)).parent; 454 | folderSafe = createFolder(parentFolder); 455 | retVal.error = (retVal.error || !folderSafe); 456 | } 457 | 458 | if (folderSafe) { 459 | 460 | storeHistory(); 461 | 462 | if (prefs.scale) 463 | scaleImage(); 464 | 465 | if (prefs.padding) 466 | addPadding(); 467 | 468 | saveImage(fileName); 469 | 470 | restoreHistory(); 471 | 472 | ++retVal.count; 473 | 474 | } 475 | 476 | if (prefs.trim == TrimPrefType.INDIVIDUAL) { 477 | //undo(doc); 478 | restoreHistory() 479 | } 480 | 481 | layer.visible = false; 482 | } 483 | } else { 484 | retVal.error = true; 485 | } 486 | 487 | if (progressBarWindow) { 488 | updateProgressBar(progressBarWindow, "Exporting " + (i + 1) + " of " + count + "..."); 489 | repaintProgressBar(progressBarWindow); 490 | if (userCancelled) { 491 | break; 492 | } 493 | } 494 | } 495 | 496 | if (progressBarWindow) { 497 | progressBarWindow.hide(); 498 | } 499 | } 500 | 501 | return retVal; 502 | } 503 | 504 | 505 | function scaleImage() { 506 | var width = app.activeDocument.width.as("px") * (prefs.scaleValue / 100); 507 | app.activeDocument.resizeImage(UnitValue(width, "px"), null, null, ResampleMethod.BICUBICSHARPER); 508 | } 509 | 510 | function addPadding() { 511 | oldH = app.activeDocument.height.as("px"); 512 | oldW = app.activeDocument.width.as("px"); 513 | 514 | 515 | var width = (app.activeDocument.width.as("px")) + (prefs.paddingValue * 2); 516 | var height = (app.activeDocument.height.as("px")) + (prefs.paddingValue * 2); 517 | 518 | app.activeDocument.resizeCanvas(width, height, AnchorPosition.MIDDLECENTER); 519 | } 520 | 521 | function createFolder(folder) { 522 | var result = true; 523 | var missingFolders = []; 524 | 525 | var parentFolder = folder; 526 | while (parentFolder) { 527 | if (!parentFolder.exists) { 528 | missingFolders.push(parentFolder); 529 | } 530 | 531 | parentFolder = parentFolder.parent; 532 | } 533 | 534 | try { 535 | for (var i = missingFolders.length - 1; i >= 0; --i) { 536 | if (!missingFolders[i].create()) { 537 | result = false; 538 | break; 539 | } 540 | } 541 | } catch (e) { 542 | result = false; 543 | } 544 | 545 | return result; 546 | } 547 | 548 | function createUniqueFolders(exportLayerTarget) { 549 | var isTargetGroup; 550 | 551 | switch (exportLayerTarget) { 552 | 553 | case ExportLayerTarget.VISIBLE_LAYERS: 554 | isTargetGroup = function(group) { 555 | return group.visible; 556 | }; 557 | break; 558 | 559 | case ExportLayerTarget.SELECTED_LAYERS: 560 | isTargetGroup = function(group) { 561 | return group.selected; 562 | }; 563 | break; 564 | 565 | default: 566 | isTargetGroup = function(group) { 567 | return true; 568 | }; 569 | break; 570 | } 571 | 572 | for (var i = 0; i < groups.length; ++i) { 573 | var group = groups[i]; 574 | if (isTargetGroup(group)) { 575 | var path = makeFolderName(group); 576 | var folder = new Folder(path); 577 | if (folder.exists && !prefs.overwrite) { 578 | var renamed = false; 579 | for (var j = 1; j <= 100; ++j) { 580 | var handle = new Folder(path + "-" + padder(j, 3)); 581 | if (!handle.exists) { 582 | try { 583 | renamed = folder.rename(handle.name); 584 | } catch (e) {} 585 | break; 586 | } 587 | } 588 | 589 | if (!renamed) { 590 | return "Directory '" + folder.name + "' already exists. Failed to rename."; 591 | } 592 | } 593 | 594 | folder = new Folder(path); 595 | try { 596 | if (!folder.create()) { 597 | throw new Error(); 598 | } 599 | } catch (e) { 600 | return "Failed to create directory '" + folder.name + "'."; 601 | } 602 | } 603 | } 604 | 605 | return true; 606 | } 607 | 608 | function saveImage(fileName) { 609 | if (prefs.formatArgs instanceof ExportOptionsSaveForWeb) { 610 | // Document.exportDocument() is unreliable -- it ignores some of the export options. 611 | // Avoid it if possible. 612 | switch (prefs.format) { 613 | 614 | case "PNG-24": 615 | exportPng24AM(fileName, prefs.formatArgs); 616 | break; 617 | 618 | case "PNG-8": 619 | exportPng8AM(fileName, prefs.formatArgs); 620 | break; 621 | 622 | default: 623 | app.activeDocument.exportDocument(fileName, ExportType.SAVEFORWEB, prefs.formatArgs); 624 | break; 625 | } 626 | } else { 627 | app.activeDocument.saveAs(fileName, prefs.formatArgs, true, LetterCase.toExtensionType(prefs.namingLetterCase)); 628 | } 629 | 630 | return true; 631 | } 632 | 633 | function makeFolderName(group) { 634 | var folderName = makeValidFileName(group.layer.name, prefs.replaceSpaces); 635 | if (folderName.length == 0) { 636 | folderName = "Group"; 637 | } 638 | 639 | folderName = prefs.filePath + "/" + folderName; 640 | 641 | return folderName; 642 | } 643 | 644 | function makeFileNameFromIndex(index, numOfDigits, layer) { 645 | var fileName = "" + padder(index, numOfDigits); 646 | return getUniqueFileName(fileName, layer); 647 | } 648 | 649 | function makeFileNameFromLayerName(layer, stripExt) { 650 | var fileName = makeValidFileName(layer.layer.name, prefs.replaceSpaces); 651 | if (stripExt) { 652 | var dotIdx = fileName.indexOf('.'); 653 | if (dotIdx >= 0) { 654 | fileName = fileName.substring(0, dotIdx); 655 | } 656 | } 657 | if (fileName.length == 0) { 658 | fileName = "Layer"; 659 | } 660 | return getUniqueFileName(fileName, layer); 661 | } 662 | 663 | function getUniqueFileName(fileName, layer) { 664 | var ext = prefs.fileExtension; 665 | // makeValidFileName() here basically just converts the space between the prefix, the core file name and suffix, 666 | // but it's a good idea to keep file naming conventions in one place, i.e. inside makeValidFileName(), 667 | // and rely on them exclusively. 668 | var outputPrefix = prefs.groupsAsFolders ? "" : prefs.outputPrefix; 669 | var outputSuffix = prefs.groupsAsFolders ? "" : prefs.outputSuffix; 670 | fileName = makeValidFileName(outputPrefix + fileName + outputSuffix, prefs.replaceSpaces); 671 | if (prefs.namingLetterCase == LetterCase.LOWERCASE) { 672 | fileName = fileName.toLowerCase(); 673 | ext = ext.toLowerCase(); 674 | } else if (prefs.namingLetterCase == LetterCase.UPPERCASE) { 675 | fileName = fileName.toUpperCase(); 676 | ext = ext.toUpperCase(); 677 | } 678 | 679 | var localFolders = ""; 680 | if (prefs.groupsAsFolders) { 681 | var parent = layer.parent; 682 | while (parent) { 683 | localFolders = makeValidFileName(parent.layer.name, prefs.replaceSpaces) + "/" + localFolders; 684 | parent = parent.parent; 685 | } 686 | } else if (prefs.topGroupAsFolder) { 687 | var topGroup = null; 688 | var parent = layer.parent; 689 | 690 | while (parent) { 691 | if (parent.layer && parent.layer.typename == "LayerSet" && parent.layer.parent == activeDocument) { 692 | topGroup = parent.layer; 693 | break; 694 | } 695 | parent = parent.parent; 696 | } 697 | if (topGroup) { 698 | var filePath = prefs.filePath + "/" + topGroup.name; 699 | var subFolder = new Folder(filePath); 700 | if (!subFolder.exists) { 701 | subFolder.create(); 702 | } 703 | localFolders = topGroup.name + "/"; 704 | } 705 | } 706 | 707 | fileName = prefs.filePath + "/" + localFolders + fileName; 708 | 709 | // Check if the file already exists. In such case a numeric suffix will be added to disambiguate. 710 | var uniqueName = fileName; 711 | for (var i = 1; i <= 100; ++i) { 712 | var handle = File(uniqueName + ext); 713 | if (handle.exists && !prefs.overwrite) { 714 | uniqueName = fileName + "-" + padder(i, 3); 715 | } else { 716 | return handle; 717 | } 718 | } 719 | 720 | return false; 721 | } 722 | 723 | function mergeTopGroups(doc) { 724 | var layerSets = doc.layerSets; 725 | var layerSetList = []; 726 | for (var i = 0; i < layerSets.length; i++) { 727 | layerSetList.push(layerSets[i]); 728 | } 729 | for (var i = 0; i < layerSetList.length; i++) { 730 | var layerSet = layerSetList[i]; 731 | var layers = layerSet.layers; 732 | if (layers.length > 0) { 733 | var visible = layerSet.visible; 734 | var layer = layerSet.merge(); 735 | layer.visible = visible; 736 | } 737 | } 738 | } 739 | 740 | function forEachLayer(inCollection, doFunc, result, traverseInvisibleSets) { 741 | var length = inCollection.length; 742 | for (var i = 0; i < length; ++i) { 743 | var layer = inCollection[i]; 744 | if (layer.layer && layer.layer.typename == "LayerSet") { 745 | if (traverseInvisibleSets || layer.visible) { 746 | result = forEachLayer(layer.layers, doFunc, result, traverseInvisibleSets); 747 | } 748 | } else { 749 | result = doFunc(layer, result); 750 | } 751 | } 752 | 753 | return result; 754 | } 755 | 756 | // Indexed access to Layers via the default provided API is very slow, so all layers should be 757 | // collected into a separate collection beforehand and that should be accessed repeatedly. 758 | function collectLayers(progressBarWindow) { 759 | // proxy to lower level ActionManager code 760 | return collectLayersAM(progressBarWindow); 761 | } 762 | 763 | function countLayers(progressBarWindow) { 764 | // proxy to lower level ActionManager code 765 | return countLayersAM(progressBarWindow); 766 | } 767 | 768 | function undo(doc) { 769 | doc.activeHistoryState = doc.historyStates[doc.historyStates.length - 2]; 770 | } 771 | 772 | function makeInvisible(layer) { 773 | layer.layer.visible = false; 774 | 775 | var current = layer.parent; 776 | while (current) { 777 | if (!current.layer.visible) { 778 | current.layer.visible = false; 779 | } 780 | current = current.parent; 781 | } 782 | } 783 | 784 | function makeVisible(layer) { 785 | layer.layer.visible = true; 786 | 787 | var current = layer.parent; 788 | while (current) { 789 | if (!current.layer.visible) { 790 | current.layer.visible = true; 791 | } 792 | current = current.parent; 793 | } 794 | } 795 | 796 | function isAdjustmentLayer(layer) { 797 | switch (layer.kind) { 798 | 799 | case LayerKind.BRIGHTNESSCONTRAST: 800 | case LayerKind.CHANNELMIXER: 801 | case LayerKind.COLORBALANCE: 802 | case LayerKind.CURVES: 803 | case LayerKind.GRADIENTMAP: 804 | case LayerKind.HUESATURATION: 805 | case LayerKind.INVERSION: 806 | case LayerKind.LEVELS: 807 | case LayerKind.POSTERIZE: 808 | case LayerKind.SELECTIVECOLOR: 809 | case LayerKind.THRESHOLD: 810 | return true; 811 | 812 | default: 813 | return false; 814 | } 815 | 816 | } 817 | 818 | // 819 | // User interface 820 | // 821 | 822 | function createProgressBar() { 823 | // read progress bar resource 824 | var rsrcFile = new File(env.scriptFileDirectory + "/" + encodeURI("Export Layers To Files (Fast)-progress_bar.json")); 825 | var rsrcString = loadResource(rsrcFile); 826 | if (!rsrcString) { 827 | return false; 828 | } 829 | 830 | // create window 831 | var win; 832 | try { 833 | win = new Window(rsrcString); 834 | } catch (e) { 835 | alert("Progress bar resource is corrupt! Please, redownload the script with all files.", "Error", true); 836 | return false; 837 | } 838 | 839 | win.barRow.cancelBtn.onClick = function() { 840 | userCancelled = true; 841 | }; 842 | 843 | win.onResizing = win.onResize = function() { 844 | this.layout.resize(); 845 | } 846 | 847 | win.onClose = function() { 848 | userCancelled = true; 849 | return false; 850 | }; 851 | return win; 852 | } 853 | 854 | function showProgressBar(win, message, maxValue) { 855 | win.lblMessage.text = message; 856 | win.barRow.bar.maxvalue = maxValue; 857 | win.barRow.bar.value = 0; 858 | 859 | win.center(); 860 | win.show(); 861 | repaintProgressBar(win, true); 862 | } 863 | 864 | function updateProgressBar(win, message) { 865 | ++win.barRow.bar.value; 866 | if (message) { 867 | win.lblMessage.text = message; 868 | } 869 | } 870 | 871 | function repaintProgressBar(win, force /* = false*/ ) { 872 | if (env.version >= 11) { // CS4 added support for UI updates; the previous method became unbearably slow, as is app.refresh() 873 | if (force) { 874 | app.refresh(); 875 | } else { 876 | win.update(); 877 | } 878 | } else { 879 | // CS3 and below 880 | var d = new ActionDescriptor(); 881 | d.putEnumerated(app.stringIDToTypeID('state'), app.stringIDToTypeID('state'), app.stringIDToTypeID('redrawComplete')); 882 | app.executeAction(app.stringIDToTypeID('wait'), d, DialogModes.NO); 883 | } 884 | } 885 | 886 | function showDialog() { 887 | // read dialog resource 888 | var rsrcFile = new File(env.scriptFileDirectory + "/" + encodeURI("Export Layers To Files (Fast)-dialog.json")); 889 | var rsrcString = loadResource(rsrcFile); 890 | if (!rsrcString) { 891 | return false; 892 | } 893 | 894 | // build dialogue 895 | var dlg; 896 | try { 897 | dlg = new Window(rsrcString); 898 | } catch (e) { 899 | alert("Dialog resource is corrupt! Please, redownload the script with all files.", "Error", true); 900 | return false; 901 | } 902 | 903 | // destination path 904 | dlg.funcArea.content.grpDest.txtDest.text = prefs.filePath.fsName; 905 | dlg.funcArea.content.grpDest.btnDest.onClick = function() { 906 | var newFilePath = Folder.selectDialog("Select destination folder", prefs.filePath); 907 | if (newFilePath) { 908 | prefs.filePath = newFilePath; 909 | dlg.funcArea.content.grpDest.txtDest.text = newFilePath.fsName; 910 | } 911 | }; 912 | 913 | dlg.funcArea.buttons.cbOverwrite.onClick = function() { 914 | prefs.overwrite = this.value; 915 | }; 916 | 917 | //Scale section 918 | dlg.funcArea.content.grpScale.cbScale.value = prefs.scale; 919 | dlg.funcArea.content.grpScale.editScale.enabled = prefs.scale; 920 | dlg.funcArea.content.grpScale.editScale.text = prefs.scaleValue; 921 | dlg.funcArea.content.grpScale.cbScale.onClick = function() { 922 | prefs.scale = this.value; 923 | dlg.funcArea.content.grpScale.editScale.enabled = this.value; 924 | }; 925 | 926 | dlg.funcArea.content.grpScale.editScale.onChanging = function() { 927 | prefs.scaleValue = parseInt(this.text); 928 | }; 929 | 930 | // layer subset selection 931 | dlg.funcArea.content.grpLayers.radioLayersAll.onClick = function() { 932 | prefs.exportLayerTarget = ExportLayerTarget.ALL_LAYERS; 933 | dlg.funcArea.content.grpBgFgLayer.cbBgLayer.enabled = (layerCount > 1); 934 | dlg.funcArea.content.grpBgFgLayer.cbFgLayer.enabled = (layerCount > 1); 935 | }; 936 | dlg.funcArea.content.grpLayers.radioLayersVis.onClick = function() { 937 | prefs.exportLayerTarget = ExportLayerTarget.VISIBLE_LAYERS; 938 | dlg.funcArea.content.grpBgFgLayer.cbBgLayer.enabled = (visibleLayerCount > 1); 939 | dlg.funcArea.content.grpBgFgLayer.cbFgLayer.enabled = (visibleLayerCount > 1); 940 | }; 941 | dlg.funcArea.content.grpLayers.radioLayersSel.onClick = function() { 942 | prefs.exportLayerTarget = ExportLayerTarget.SELECTED_LAYERS; 943 | dlg.funcArea.content.grpBgFgLayer.cbBgLayer.enabled = false; 944 | dlg.funcArea.content.grpBgFgLayer.cbFgLayer.enabled = false; 945 | dlg.funcArea.content.grpBgFgLayer.cbBgLayer.value = false; 946 | dlg.funcArea.content.grpBgFgLayer.cbFgLayer.value = false; 947 | }; 948 | dlg.funcArea.content.grpLayers.radioLayersVis.enabled = (visibleLayerCount > 0); 949 | dlg.funcArea.content.grpLayers.radioLayersSel.enabled = (selectedLayerCount > 0); 950 | 951 | dlg.funcArea.content.grpLayers.cbIgnoreLayers.value = prefs.ignoreLayers; 952 | dlg.funcArea.content.grpLayers.EditIgnoreLayers.enabled = prefs.ignoreLayers; 953 | 954 | dlg.funcArea.content.grpLayers.EditIgnoreLayers.text = prefs.ignoreLayersString; 955 | dlg.funcArea.content.grpLayers.cbIgnoreLayers.onClick = function() { 956 | dlg.funcArea.content.grpLayers.EditIgnoreLayers.enabled = this.value; 957 | prefs.ignoreLayers = !this.value; 958 | }; 959 | 960 | dlg.funcArea.content.grpLayers.EditIgnoreLayers.onChange = function() { 961 | prefs.ignoreLayersString = this.text; 962 | }; 963 | 964 | var formatDropDown = dlg.funcArea.content.grpFileType.drdFileType; 965 | var optionsPanel = dlg.funcArea.content.pnlOptions; 966 | 967 | // Add your format definition here; the rest is taken care of. 968 | var formatFuncs = [ 969 | getFormatOptsPNG24, 970 | getFormatOptsPNG8, 971 | getFormatOptsJPEG, 972 | getFormatOptsTarga, 973 | getFormatOptsBMP 974 | ]; 975 | var formatOpts = [ 976 | { opt: null, controlRoot: null }, 977 | { opt: null, controlRoot: null }, 978 | { opt: null, controlRoot: null }, 979 | { opt: null, controlRoot: null }, 980 | { opt: null, controlRoot: null } 981 | ]; 982 | for (var i = 0, len = formatFuncs.length; i < len; ++i) { 983 | var optionsRoot = optionsPanel.add("group"); 984 | optionsRoot.orientation = "column"; 985 | optionsRoot.alignChildren = "left"; 986 | 987 | formatOpts[i].controlRoot = optionsRoot; 988 | formatOpts[i].opt = formatFuncs[i](); 989 | formatOpts[i].opt.dialogParams(optionsRoot); 990 | 991 | formatDropDown.add("item", formatOpts[i].opt.type); 992 | } 993 | 994 | // show proper file type options 995 | formatDropDown.onChange = function() { 996 | // Note: There's a bug in CS5 and CC where ListItem.selected doesn't report correct value in onChange(). 997 | // A workaround is to rely on DropDownList.selection instead. 998 | for (var i = formatOpts.length - 1; i >= 0; --i) { 999 | formatOpts[i].controlRoot.hide(); 1000 | } 1001 | formatOpts[this.selection.index].controlRoot.show(); 1002 | }; 1003 | 1004 | formatDropDown.selection = 0; 1005 | 1006 | // file name prefix 1007 | dlg.funcArea.content.grpPrefix.editPrefix.onChange = function() { 1008 | this.text = makeValidFileName(this.text, prefs.replaceSpaces); 1009 | }; 1010 | 1011 | // file name suffix 1012 | dlg.funcArea.content.grpPrefix.editSuffix.onChange = function() { 1013 | this.text = makeValidFileName(this.text, prefs.replaceSpaces); 1014 | }; 1015 | 1016 | dlg.funcArea.content.grpFolderTree.cbFolderTree.onClick = function() { 1017 | dlg.funcArea.content.grpPrefix.editPrefix.enabled = !this.value; 1018 | dlg.funcArea.content.grpPrefix.editSuffix.enabled = !this.value; 1019 | 1020 | var cbTopGroupAsFolder = dlg.funcArea.content.grpFolderTree.cbTopGroupAsFolder; 1021 | var cbTopGroupAsLayer = dlg.funcArea.content.grpFolderTree.cbTopGroupAsLayer; 1022 | 1023 | cbTopGroupAsFolder.enabled = !this.value; 1024 | cbTopGroupAsLayer.enabled = !this.value; 1025 | }; 1026 | 1027 | dlg.funcArea.content.grpFolderTree.cbTopGroupAsFolder.onClick = function() { 1028 | dlg.funcArea.content.grpPrefix.editPrefix.enabled = !this.value; 1029 | dlg.funcArea.content.grpPrefix.editSuffix.enabled = !this.value; 1030 | 1031 | dlg.funcArea.content.grpFolderTree.cbFolderTree.enabled = !this.value; 1032 | dlg.funcArea.content.grpFolderTree.cbTopGroupAsLayer.enabled = !this.value; 1033 | }; 1034 | 1035 | dlg.funcArea.content.grpFolderTree.cbTopGroupAsLayer.onClick = function() { 1036 | dlg.funcArea.content.grpPrefix.editPrefix.enabled = !this.value; 1037 | dlg.funcArea.content.grpPrefix.editSuffix.enabled = !this.value; 1038 | 1039 | dlg.funcArea.content.grpFolderTree.cbFolderTree.enabled = !this.value; 1040 | dlg.funcArea.content.grpFolderTree.cbTopGroupAsFolder.enabled = !this.value; 1041 | }; 1042 | 1043 | // file naming options 1044 | dlg.funcArea.content.grpNaming.drdNaming.selection = 0; 1045 | dlg.funcArea.content.grpLetterCase.drdLetterCase.selection = 0; 1046 | 1047 | dlg.funcArea.content.grpNaming.cbNaming.onClick = function() { 1048 | prefs.replaceSpaces = !this.value; 1049 | if (prefs.replaceSpaces) { 1050 | dlg.funcArea.content.grpNaming.radioUnderscore.show(); 1051 | dlg.funcArea.content.grpNaming.radioHyphen.show(); 1052 | } else { 1053 | dlg.funcArea.content.grpNaming.radioUnderscore.hide(); 1054 | dlg.funcArea.content.grpNaming.radioHyphen.hide(); 1055 | } 1056 | }; 1057 | 1058 | dlg.funcArea.content.grpNaming.radioUnderscore.onClick = function() { 1059 | prefs.delimiter = '_'; 1060 | }; 1061 | 1062 | dlg.funcArea.content.grpNaming.radioHyphen.onClick = function() { 1063 | prefs.delimiter = '-'; 1064 | }; 1065 | 1066 | // trimming 1067 | dlg.funcArea.content.grpTrim.drdTrim.onChange = function() { 1068 | if (TrimPrefType.forIndex(this.selection.index) == TrimPrefType.INDIVIDUAL) { 1069 | dlg.funcArea.content.grpTrim.cbTrim.show(); 1070 | } else { 1071 | dlg.funcArea.content.grpTrim.cbTrim.hide(); 1072 | } 1073 | } 1074 | 1075 | dlg.funcArea.content.grpTrim.drdTrim.selection = 0; 1076 | 1077 | //Padding setting 1078 | dlg.funcArea.content.grpPadding.cbPadding.value = prefs.padding; 1079 | 1080 | dlg.funcArea.content.grpPadding.editPadding.enabled = prefs.padding; 1081 | dlg.funcArea.content.grpPadding.editPadding.text = prefs.paddingValue; 1082 | 1083 | dlg.funcArea.content.grpPadding.cbPadding.onClick = function() { 1084 | prefs.padding = this.value; 1085 | dlg.funcArea.content.grpPadding.editPadding.enabled = this.value; 1086 | }; 1087 | 1088 | dlg.funcArea.content.grpPadding.editPadding.onChanging = function() { 1089 | prefs.paddingValue = parseInt(this.text); 1090 | }; 1091 | 1092 | 1093 | // background layer setting 1094 | dlg.funcArea.content.grpBgFgLayer.cbBgLayer.enabled = (layerCount > 1); 1095 | dlg.funcArea.content.grpBgFgLayer.cbFgLayer.enabled = (layerCount > 1); 1096 | 1097 | // buttons 1098 | dlg.funcArea.buttons.btnRun.onClick = function() { 1099 | // collect arguments for saving and proceed 1100 | 1101 | prefs.outputPrefix = dlg.funcArea.content.grpPrefix.editPrefix.text; 1102 | if (prefs.outputPrefix.length > 0) { 1103 | prefs.outputPrefix += " "; 1104 | } 1105 | 1106 | prefs.outputSuffix = dlg.funcArea.content.grpPrefix.editSuffix.text; 1107 | if (prefs.outputSuffix.length > 0) { 1108 | prefs.outputSuffix = " " + prefs.outputSuffix; 1109 | } 1110 | 1111 | prefs.groupsAsFolders = dlg.funcArea.content.grpFolderTree.cbFolderTree.value; 1112 | 1113 | prefs.naming = FileNameType.forIndex(dlg.funcArea.content.grpNaming.drdNaming.selection.index); 1114 | prefs.namingLetterCase = LetterCase.forIndex(dlg.funcArea.content.grpLetterCase.drdLetterCase.selection.index); 1115 | prefs.trim = TrimPrefType.forIndex(dlg.funcArea.content.grpTrim.drdTrim.selection.index); 1116 | 1117 | var cbScale = dlg.funcArea.content.grpScale.cbScale; 1118 | prefs.scale = (cbScale.value && cbScale.enabled); 1119 | 1120 | prefs.scaleValue = parseInt(dlg.funcArea.content.grpScale.editScale.text); 1121 | 1122 | prefs.forceTrimMethod = dlg.funcArea.content.grpTrim.cbTrim.value; 1123 | var cbBgLayer = dlg.funcArea.content.grpBgFgLayer.cbBgLayer; 1124 | var cbFgLayer = dlg.funcArea.content.grpBgFgLayer.cbFgLayer; 1125 | prefs.bgLayer = (cbBgLayer.value && cbBgLayer.enabled); 1126 | prefs.fgLayer = (cbFgLayer.value && cbFgLayer.enabled); 1127 | 1128 | var cbTopGroupAsLayer = dlg.funcArea.content.grpFolderTree.cbTopGroupAsLayer; 1129 | prefs.topGroupAsLayer = (cbTopGroupAsLayer.value && cbTopGroupAsLayer.enabled); 1130 | 1131 | var cbTopGroupAsFolder = dlg.funcArea.content.grpFolderTree.cbTopGroupAsFolder; 1132 | prefs.topGroupAsFolder = (cbTopGroupAsFolder.value && cbTopGroupAsFolder.enabled); 1133 | 1134 | prefs.filePath = dlg.funcArea.content.grpDest.txtDest.text; 1135 | var destFolder = new Folder(prefs.filePath); 1136 | if (!destFolder.exists) { 1137 | destFolder.create(); 1138 | } 1139 | 1140 | var selIdx = formatDropDown.selection.index; 1141 | formatOpts[selIdx].opt.onDialogSelect(formatOpts[selIdx].controlRoot); 1142 | 1143 | saveSettings(dlg, formatOpts); 1144 | 1145 | dlg.close(1); 1146 | }; 1147 | dlg.funcArea.buttons.btnCancel.onClick = function() { 1148 | dlg.close(0); 1149 | }; 1150 | 1151 | dlg.funcArea.buttons.btnSettings.enabled = env.cs3OrHigher; 1152 | dlg.funcArea.buttons.btnSettings.onClick = function() { 1153 | saveSettings(dlg, formatOpts); 1154 | dlg.close(0); 1155 | }; 1156 | 1157 | // warning message 1158 | dlg.warning.message.text = formatString(dlg.warning.message.text, layerCount, visibleLayerCount, selectedLayerCount); 1159 | 1160 | try { 1161 | applySettings(dlg, formatOpts); 1162 | } catch (err) { 1163 | alert("Failed to restore previous settings. Default settings applied.\n\n(Error: " + err.toString() + ")", "Settings not restored", true); 1164 | } 1165 | 1166 | dlg.center(); 1167 | return dlg.show(); 1168 | } 1169 | 1170 | function applySettings(dlg, formatOpts) { 1171 | if (!env.cs3OrHigher) { 1172 | return; 1173 | } 1174 | 1175 | var settings = getSettings(formatOpts); 1176 | if (settings == null) { 1177 | return; 1178 | } 1179 | 1180 | with(dlg.funcArea.content) { 1181 | // Common settings 1182 | 1183 | var destFolder = new Folder(settings.destination); 1184 | if (destFolder.exists) { 1185 | grpDest.txtDest.text = destFolder.fsName; 1186 | prefs.filePath = destFolder; 1187 | } 1188 | 1189 | switch (settings.exportLayerTarget) { 1190 | 1191 | case ExportLayerTarget.VISIBLE_LAYERS: 1192 | if (grpLayers.radioLayersVis.enabled) { 1193 | grpLayers.radioLayersVis.notify(); 1194 | } 1195 | break; 1196 | 1197 | case ExportLayerTarget.SELECTED_LAYERS: 1198 | if (grpLayers.radioLayersSel.enabled) { 1199 | grpLayers.radioLayersSel.notify(); 1200 | } 1201 | break; 1202 | } 1203 | 1204 | // option to ignore layer with name starting with a special str 1205 | grpLayers.EditIgnoreLayers.text = settings.ignoreLayersString; 1206 | grpLayers.EditIgnoreLayers.notify(); 1207 | 1208 | 1209 | var drdNamingIdx = FileNameType.getIndex(settings.nameFiles); 1210 | grpNaming.drdNaming.selection = (drdNamingIdx >= 0) ? drdNamingIdx : 0; 1211 | 1212 | if (grpNaming.cbNaming.value != settings.allowSpaces) { 1213 | grpNaming.cbNaming.notify(); 1214 | } 1215 | 1216 | if (dlg.funcArea.buttons.cbOverwrite.value != settings.overwrite) { 1217 | dlg.funcArea.buttons.cbOverwrite.notify(); 1218 | } 1219 | 1220 | var drdLetterCaseIdx = LetterCase.getIndex(settings.letterCase); 1221 | grpLetterCase.drdLetterCase.selection = (drdLetterCaseIdx >= 0) ? drdLetterCaseIdx : 0; 1222 | 1223 | grpPrefix.editPrefix.text = settings.outputPrefix; 1224 | grpPrefix.editPrefix.notify("onChange"); 1225 | if (grpFolderTree.cbFolderTree.value != settings.groupsAsFolders) { 1226 | grpFolderTree.cbFolderTree.notify(); 1227 | } 1228 | if (grpFolderTree.cbTopGroupAsFolder.value != settings.topGroupAsFolder) { 1229 | grpFolderTree.cbTopGroupAsFolder.notify(); 1230 | } 1231 | if (grpFolderTree.cbTopGroupAsLayer.value != settings.topGroupAsLayer) { 1232 | grpFolderTree.cbTopGroupAsLayer.notify(); 1233 | } 1234 | 1235 | grpPrefix.editSuffix.text = settings.outputSuffix; 1236 | grpPrefix.editSuffix.notify("onChange"); 1237 | 1238 | var drdTrimIdx = TrimPrefType.getIndex(settings.trim); 1239 | grpTrim.drdTrim.selection = (drdTrimIdx >= 0) ? drdTrimIdx : 0; 1240 | grpTrim.cbTrim.value = settings.forceTrimMethod; 1241 | 1242 | //Padding 1243 | 1244 | grpPadding.cbPadding.value = settings.padding; 1245 | grpPadding.editPadding.enabled = settings.padding; 1246 | grpPadding.editPadding.text = settings.paddingValue + ""; 1247 | 1248 | 1249 | //scale settigns: 1250 | 1251 | grpScale.cbScale.value = settings.scale; 1252 | grpScale.editScale.enabled = settings.scale; 1253 | grpScale.editScale.text = settings.scaleValue; 1254 | 1255 | 1256 | grpBgFgLayer.cbBgLayer.value = settings.exportBackground; 1257 | grpBgFgLayer.cbFgLayer.value = settings.exportForeground; 1258 | 1259 | grpFolderTree.cbTopGroupAsFolder.value = settings.topGroupAsFolder; 1260 | grpFolderTree.cbTopGroupAsLayer.value = settings.topGroupAsLayer; 1261 | var drdFileTypeIdx = 0; 1262 | for (var i = 0; i < formatOpts.length; ++i) { 1263 | if (formatOpts[i].opt.type == settings.fileType) { 1264 | drdFileTypeIdx = i; 1265 | break; 1266 | } 1267 | } 1268 | grpFileType.drdFileType.selection = drdFileTypeIdx; 1269 | 1270 | // File format specific 1271 | 1272 | for (var i = 0; i < formatOpts.length; ++i) { 1273 | formatOpts[i].opt.applySettings(settings, formatOpts[i].controlRoot); 1274 | } 1275 | } 1276 | } 1277 | 1278 | function saveSettings(dlg, formatOpts) { 1279 | if (!env.cs3OrHigher) { 1280 | return; 1281 | } 1282 | 1283 | // Collect settings from the dialog controls. 1284 | 1285 | var desc = new ActionDescriptor(); 1286 | 1287 | with(dlg.funcArea.content) { 1288 | // common 1289 | 1290 | var exportLayerTarget = ExportLayerTarget.ALL_LAYERS; 1291 | if (grpLayers.radioLayersVis.value) { 1292 | exportLayerTarget = ExportLayerTarget.VISIBLE_LAYERS; 1293 | } else if (grpLayers.radioLayersSel.value) { 1294 | exportLayerTarget = ExportLayerTarget.SELECTED_LAYERS; 1295 | } 1296 | 1297 | desc.putString(DEFAULT_SETTINGS.destination, grpDest.txtDest.text); 1298 | desc.putString(DEFAULT_SETTINGS.ignoreLayersString, grpLayers.EditIgnoreLayers.text); 1299 | desc.putBoolean(DEFAULT_SETTINGS.overwrite, dlg.funcArea.buttons.cbOverwrite.value); 1300 | desc.putInteger(DEFAULT_SETTINGS.exportLayerTarget, exportLayerTarget); 1301 | desc.putInteger(DEFAULT_SETTINGS.nameFiles, FileNameType.forIndex(grpNaming.drdNaming.selection.index)); 1302 | desc.putBoolean(DEFAULT_SETTINGS.allowSpaces, grpNaming.cbNaming.value); 1303 | desc.putInteger(DEFAULT_SETTINGS.letterCase, LetterCase.forIndex(grpLetterCase.drdLetterCase.selection.index)); 1304 | desc.putString(DEFAULT_SETTINGS.outputPrefix, grpPrefix.editPrefix.text); 1305 | desc.putBoolean(DEFAULT_SETTINGS.groupsAsFolders, grpFolderTree.cbFolderTree.value); 1306 | desc.putBoolean(DEFAULT_SETTINGS.topGroupAsFolder, grpFolderTree.cbTopGroupAsFolder.value); 1307 | desc.putBoolean(DEFAULT_SETTINGS.topGroupAsLayer, grpFolderTree.cbTopGroupAsLayer.value); 1308 | desc.putString(DEFAULT_SETTINGS.outputSuffix, grpPrefix.editSuffix.text); 1309 | desc.putInteger(DEFAULT_SETTINGS.trim, TrimPrefType.forIndex(grpTrim.drdTrim.selection.index)); 1310 | 1311 | desc.putBoolean(DEFAULT_SETTINGS.padding, grpPadding.cbPadding.value); 1312 | desc.putInteger(DEFAULT_SETTINGS.paddingValue, parseInt(grpPadding.editPadding.text)); 1313 | 1314 | desc.putInteger(DEFAULT_SETTINGS.scaleValue, parseFloat(grpScale.editScale.text)); 1315 | desc.putBoolean(DEFAULT_SETTINGS.scale, grpScale.cbScale.value); 1316 | desc.putBoolean(DEFAULT_SETTINGS.exportBackground, grpBgFgLayer.cbBgLayer.value); 1317 | desc.putBoolean(DEFAULT_SETTINGS.exportForeground, grpBgFgLayer.cbFgLayer.value); 1318 | desc.putString(DEFAULT_SETTINGS.fileType, formatOpts[grpFileType.drdFileType.selection.index].opt.type); 1319 | desc.putBoolean(DEFAULT_SETTINGS.forceTrimMethod, grpTrim.cbTrim.value); 1320 | 1321 | // per file format 1322 | 1323 | for (var i = 0; i < formatOpts.length; ++i) { 1324 | formatOpts[i].opt.packSettings(desc, formatOpts[i].controlRoot); 1325 | } 1326 | } 1327 | 1328 | // Save settings. 1329 | 1330 | // "true" means setting persists across Photoshop launches. 1331 | app.putCustomOptions(USER_SETTINGS_ID, desc, true); 1332 | } 1333 | 1334 | function getSettings(formatOpts) { 1335 | if (!env.cs3OrHigher) { 1336 | return null; 1337 | } 1338 | 1339 | var desc; 1340 | var result = null; 1341 | try { 1342 | // might throw if settings not present (not saved previously) 1343 | desc = app.getCustomOptions(USER_SETTINGS_ID); 1344 | 1345 | // might throw if format changed or got corrupt 1346 | result = { 1347 | // common 1348 | topGroupAsLayer: desc.getBoolean(DEFAULT_SETTINGS.topGroupAsLayer), 1349 | topGroupAsFolder: desc.getBoolean(DEFAULT_SETTINGS.topGroupAsFolder), 1350 | destination: desc.getString(DEFAULT_SETTINGS.destination), 1351 | overwrite: desc.getBoolean(DEFAULT_SETTINGS.overwrite), 1352 | exportLayerTarget: desc.getInteger(DEFAULT_SETTINGS.exportLayerTarget), 1353 | nameFiles: desc.getInteger(DEFAULT_SETTINGS.nameFiles), 1354 | allowSpaces: desc.getBoolean(DEFAULT_SETTINGS.allowSpaces), 1355 | letterCase: desc.getInteger(DEFAULT_SETTINGS.letterCase), 1356 | outputPrefix: desc.getString(DEFAULT_SETTINGS.outputPrefix), 1357 | groupsAsFolders: desc.getBoolean(DEFAULT_SETTINGS.groupsAsFolders), 1358 | outputSuffix: desc.getString(DEFAULT_SETTINGS.outputSuffix), 1359 | trim: desc.getInteger(DEFAULT_SETTINGS.trim), 1360 | scale: desc.getBoolean(DEFAULT_SETTINGS.scale), 1361 | scaleValue: desc.getInteger(DEFAULT_SETTINGS.scaleValue), 1362 | padding: desc.getBoolean(DEFAULT_SETTINGS.padding), 1363 | paddingValue: desc.getInteger(DEFAULT_SETTINGS.paddingValue), 1364 | exportBackground: desc.getBoolean(DEFAULT_SETTINGS.exportBackground), 1365 | exportForeground: desc.getBoolean(DEFAULT_SETTINGS.exportForeground), 1366 | fileType: desc.getString(DEFAULT_SETTINGS.fileType), 1367 | forceTrimMethod: desc.getBoolean(DEFAULT_SETTINGS.forceTrimMethod), 1368 | ignoreLayersString: desc.getString(DEFAULT_SETTINGS.ignoreLayersString) 1369 | 1370 | // per file format filled below 1371 | 1372 | // format: [] 1373 | }; 1374 | 1375 | result.format = []; 1376 | for (var i = 0; i < formatOpts.length; ++i) { 1377 | result.format[formatOpts[i].opt.type] = formatOpts[i].opt.unpackSettings(desc); 1378 | } 1379 | } catch (e) { 1380 | return null; 1381 | } 1382 | 1383 | return result; 1384 | } 1385 | 1386 | // Format specific definitions 1387 | 1388 | // Clone this function-class to add a new export file format 1389 | function getFormatOptsTarga() { 1390 | return { 1391 | type: "TGA", 1392 | 1393 | // Dialog GUI 1394 | dialogParams: function(parent) { 1395 | var depth = parent.add("group"); 1396 | depth.add("statictext", undefined, "Depth:"); 1397 | var bitsPerPixelLabels = ["16 bit", "24 bit", "32 bit"]; 1398 | parent.bitsPerPixel = depth.add("dropdownlist", undefined, bitsPerPixelLabels); 1399 | parent.bitsPerPixel.selection = 2; 1400 | 1401 | parent.alpha = parent.add("checkbox", undefined, "With alpha channel"); 1402 | parent.alpha.value = true; 1403 | 1404 | parent.rle = parent.add("checkbox", undefined, "RLE compression"); 1405 | parent.rle.value = true; 1406 | }, 1407 | 1408 | // Reaction to dialog confirmation 1409 | onDialogSelect: function(parent) { 1410 | prefs.format = "TGA"; 1411 | prefs.fileExtension = ".tga"; 1412 | prefs.formatArgs = new TargaSaveOptions(); 1413 | prefs.formatArgs.alphaChannels = parent.alpha.value; 1414 | prefs.formatArgs.rleCompression = parent.rle.value; 1415 | var resolution_enum = [TargaBitsPerPixels.SIXTEEN, TargaBitsPerPixels.TWENTYFOUR, TargaBitsPerPixels.THIRTYTWO]; 1416 | prefs.formatArgs.resolution = resolution_enum[parent.bitsPerPixel.selection.index]; 1417 | }, 1418 | 1419 | settingsKeys: { 1420 | depth: app.stringIDToTypeID("tgaDepth"), 1421 | alpha: app.stringIDToTypeID("tgaAlpha"), 1422 | rle: app.stringIDToTypeID("tgaRle") 1423 | }, 1424 | 1425 | // Save settings into an ActionDescriptor 1426 | packSettings: function(desc, formatOptRoot) { 1427 | desc.putInteger(this.settingsKeys.depth, formatOptRoot.bitsPerPixel.selection.index); 1428 | desc.putBoolean(this.settingsKeys.alpha, formatOptRoot.alpha.value); 1429 | desc.putBoolean(this.settingsKeys.rle, formatOptRoot.rle.value); 1430 | }, 1431 | 1432 | // Get settings from an ActionDescriptor 1433 | unpackSettings: function(desc) { 1434 | return { 1435 | depth: desc.getInteger(this.settingsKeys.depth), 1436 | alpha: desc.getBoolean(this.settingsKeys.alpha), 1437 | rle: desc.getBoolean(this.settingsKeys.rle) 1438 | }; 1439 | }, 1440 | 1441 | // Apply settings to dialog GUI 1442 | applySettings: function(settings, formatOptRoot) { 1443 | formatOptRoot.alpha.value = settings.format[this.type].alpha; 1444 | formatOptRoot.bitsPerPixel.selection = settings.format[this.type].depth; 1445 | formatOptRoot.rle.value = settings.format[this.type].rle; 1446 | } 1447 | }; 1448 | } 1449 | 1450 | function getFormatOptsJPEG() { 1451 | return { 1452 | type: "JPG", 1453 | 1454 | // Dialog GUI 1455 | dialogParams: function(parent) { 1456 | var ROW_HEIGHT = 16; 1457 | 1458 | // quality 1459 | var row = parent.add("group"); 1460 | var qualityLabel = row.add("statictext", undefined, "Quality:"); 1461 | qualityLabel.preferredSize = [40, ROW_HEIGHT]; 1462 | parent.quality = row.add("slider", undefined, 12, 0, 12); 1463 | parent.quality.preferredSize = [140, 20]; 1464 | var qualityValue = row.add("statictext", undefined, "12"); 1465 | qualityValue.preferredSize = [30, ROW_HEIGHT]; 1466 | 1467 | parent.quality.onChanging = function() { 1468 | this.value = Math.round(this.value); 1469 | qualityValue.text = this.value; 1470 | }; 1471 | 1472 | // matte 1473 | row = parent.add("group"); 1474 | var matteLabel = row.add("statictext", undefined, "Matte:"); 1475 | matteLabel.preferredSize = [40, ROW_HEIGHT]; 1476 | parent.matte = row.add("dropdownlist", undefined, ["White", "Black", "Gray", "-", "Background", "Foreground"]); 1477 | parent.matte.selection = 0; 1478 | 1479 | // color profile 1480 | parent.icc = parent.add("checkbox", undefined, "ICC Profile"); 1481 | 1482 | // optimised 1483 | parent.optimised = parent.add("checkbox", undefined, "Optimized"); 1484 | parent.optimised.value = true; 1485 | 1486 | // progressive 1487 | parent.progressive = parent.add("checkbox", undefined, "Progressive"); 1488 | parent.progressive.onClick = function() { 1489 | parent.optimised.enabled = !this.value; 1490 | }; 1491 | }, 1492 | 1493 | // Reaction to dialog confirmation 1494 | onDialogSelect: function(parent) { 1495 | prefs.format = "JPG"; 1496 | prefs.fileExtension = ".jpg"; 1497 | prefs.formatArgs = new JPEGSaveOptions(); 1498 | var matteValue = [MatteType.WHITE, MatteType.BLACK, MatteType.SEMIGRAY, MatteType.NONE, MatteType.BACKGROUND, MatteType.FOREGROUND]; 1499 | with(prefs.formatArgs) { 1500 | quality = parent.quality.value; 1501 | matte = matteValue[parent.matte.selection.index]; 1502 | embedColorProfile = parent.icc.value; 1503 | if (parent.progressive.value) { 1504 | formatOptions = FormatOptions.PROGRESSIVE; 1505 | scans = 3; 1506 | } else if (parent.optimised.value) { 1507 | formatOptions = FormatOptions.OPTIMIZEDBASELINE; 1508 | } else { 1509 | formatOptions = FormatOptions.STANDARDBASELINE; 1510 | } 1511 | } 1512 | }, 1513 | 1514 | settingsKeys: { 1515 | quality: app.stringIDToTypeID("jpgQuality"), 1516 | matte: app.stringIDToTypeID("jpgMatte"), 1517 | icc: app.stringIDToTypeID("jpgIcc"), 1518 | optimized: app.stringIDToTypeID("jpgOptimized"), 1519 | progressive: app.stringIDToTypeID("jpgProgressive") 1520 | }, 1521 | 1522 | // Save settings into an ActionDescriptor 1523 | packSettings: function(desc, formatOptRoot) { 1524 | desc.putInteger(this.settingsKeys.quality, formatOptRoot.quality.value); 1525 | desc.putInteger(this.settingsKeys.matte, formatOptRoot.matte.selection.index); 1526 | desc.putBoolean(this.settingsKeys.icc, formatOptRoot.icc.value); 1527 | desc.putBoolean(this.settingsKeys.optimized, formatOptRoot.optimised.value); 1528 | desc.putBoolean(this.settingsKeys.progressive, formatOptRoot.progressive.value); 1529 | }, 1530 | 1531 | // Get settings from an ActionDescriptor 1532 | unpackSettings: function(desc) { 1533 | return { 1534 | quality: desc.getInteger(this.settingsKeys.quality), 1535 | matte: desc.getInteger(this.settingsKeys.matte), 1536 | icc: desc.getBoolean(this.settingsKeys.icc), 1537 | optimized: desc.getBoolean(this.settingsKeys.optimized), 1538 | progressive: desc.getBoolean(this.settingsKeys.progressive) 1539 | }; 1540 | }, 1541 | 1542 | // Apply settings to dialog GUI 1543 | applySettings: function(settings, formatOptRoot) { 1544 | var formatSettings = settings.format[this.type]; 1545 | formatOptRoot.quality.value = formatSettings.quality; 1546 | formatOptRoot.matte.selection = formatSettings.matte; 1547 | formatOptRoot.icc.value = formatSettings.icc; 1548 | formatOptRoot.optimised.value = formatSettings.optimized; 1549 | 1550 | formatOptRoot.quality.notify("onChanging"); 1551 | if (formatOptRoot.progressive.value != formatSettings.progressive) { 1552 | formatOptRoot.progressive.notify(); 1553 | } 1554 | } 1555 | }; 1556 | } 1557 | 1558 | function getFormatOptsPNG24() { 1559 | return { 1560 | type: "PNG-24", 1561 | 1562 | // Dialog GUI 1563 | dialogParams: function(parent) { 1564 | var ROW_HEIGHT = 16; 1565 | 1566 | // matte 1567 | var row = parent.add("group"); 1568 | var matteLabel = row.add("statictext", undefined, "Matte:"); 1569 | matteLabel.preferredSize = [40, ROW_HEIGHT]; 1570 | parent.matte = row.add("dropdownlist", undefined, ["White", "Black", "Gray", "-", "Background", "Foreground"]); 1571 | parent.matte.selection = 0; 1572 | parent.matte.enabled = false; 1573 | 1574 | // transparency 1575 | parent.transparency = parent.add("checkbox", undefined, "Transparency"); 1576 | parent.transparency.preferredSize = [120, ROW_HEIGHT]; 1577 | parent.transparency.value = true; 1578 | 1579 | parent.transparency.onClick = function() { 1580 | parent.matte.enabled = !this.value; 1581 | }; 1582 | 1583 | // interlaced 1584 | parent.interlaced = parent.add("checkbox", undefined, "Interlaced"); 1585 | parent.interlaced.preferredSize = [120, ROW_HEIGHT]; 1586 | }, 1587 | 1588 | // Reaction to dialog confirmation 1589 | onDialogSelect: function(parent) { 1590 | prefs.format = "PNG-24"; 1591 | prefs.fileExtension = ".png"; 1592 | 1593 | var WHITE = new RGBColor(); 1594 | WHITE.red = 255; 1595 | WHITE.green = 255; 1596 | WHITE.blue = 255; 1597 | var BLACK = new RGBColor(); 1598 | BLACK.red = 0; 1599 | BLACK.green = 0; 1600 | BLACK.blue = 0; 1601 | var GRAY = new RGBColor(); 1602 | GRAY.red = 127; 1603 | GRAY.green = 127; 1604 | GRAY.blue = 127; 1605 | 1606 | var matteColors = [WHITE, BLACK, GRAY, BLACK, app.backgroundColor.rgb, app.foregroundColor.rgb]; 1607 | 1608 | prefs.formatArgs = new ExportOptionsSaveForWeb(); 1609 | with(prefs.formatArgs) { 1610 | format = SaveDocumentType.PNG; 1611 | PNG8 = false; 1612 | interlaced = parent.interlaced.value; 1613 | transparency = parent.transparency.value; 1614 | matteColor = matteColors[parent.matte.selection.index]; 1615 | } 1616 | }, 1617 | 1618 | settingsKeys: { 1619 | matte: app.stringIDToTypeID("png24Matte"), 1620 | transparency: app.stringIDToTypeID("png24Transparency"), 1621 | interlaced: app.stringIDToTypeID("png24Interlaced") 1622 | }, 1623 | 1624 | // Save settings into an ActionDescriptor 1625 | packSettings: function(desc, formatOptRoot) { 1626 | desc.putInteger(this.settingsKeys.matte, formatOptRoot.matte.selection.index); 1627 | desc.putBoolean(this.settingsKeys.transparency, formatOptRoot.transparency.value); 1628 | desc.putBoolean(this.settingsKeys.interlaced, formatOptRoot.interlaced.value); 1629 | }, 1630 | 1631 | // Get settings from an ActionDescriptor 1632 | unpackSettings: function(desc) { 1633 | return { 1634 | matte: desc.getInteger(this.settingsKeys.matte), 1635 | transparency: desc.getBoolean(this.settingsKeys.transparency), 1636 | interlaced: desc.getBoolean(this.settingsKeys.interlaced) 1637 | }; 1638 | }, 1639 | 1640 | // Apply settings to dialog GUI 1641 | applySettings: function(settings, formatOptRoot) { 1642 | var formatSettings = settings.format[this.type]; 1643 | formatOptRoot.matte.selection = formatSettings.matte; 1644 | formatOptRoot.matte.enabled = !formatSettings.transparency; 1645 | formatOptRoot.interlaced.value = formatSettings.interlaced; 1646 | 1647 | if (formatOptRoot.transparency.value != formatSettings.transparency) { 1648 | formatOptRoot.transparency.notify(); 1649 | } 1650 | } 1651 | }; 1652 | } 1653 | 1654 | function getFormatOptsPNG8() { 1655 | return { 1656 | type: "PNG-8", 1657 | 1658 | // Dialog GUI 1659 | dialogParams: function(parent) { 1660 | var ROW_HEIGHT = 16; 1661 | var LABEL_WIDTH = 105; 1662 | 1663 | // color reduction 1664 | var row = parent.add("group"); 1665 | var crLabel = row.add("statictext", undefined, "Color reduction:"); 1666 | crLabel.preferredSize = [LABEL_WIDTH, ROW_HEIGHT]; 1667 | parent.colorReduction = row.add("dropdownlist", undefined, [ 1668 | "Perceptual", 1669 | "Selective", 1670 | "Adaptive", 1671 | "Restrictive (Web)", 1672 | "-", 1673 | "Black & White", 1674 | "Grayscale", 1675 | "Mac OS", 1676 | "Windows" 1677 | ]); 1678 | parent.colorReduction.selection = 1; 1679 | 1680 | // number of colors 1681 | row = parent.add("group"); 1682 | var colorsLabel = row.add("statictext", undefined, "Number of colors:"); 1683 | colorsLabel.preferredSize = [LABEL_WIDTH, ROW_HEIGHT]; 1684 | parent.colors = row.add("edittext", undefined, "256"); 1685 | parent.colors.preferredSize = [70, 18]; 1686 | parent.colorsLast = 256; 1687 | 1688 | parent.colors.onChange = function() { 1689 | var colorNum = parseInt(this.text, 10); 1690 | if (isNaN(colorNum)) { 1691 | colorNum = parent.colorsLast; 1692 | } else if (colorNum < 2) { 1693 | colorNum = 2; 1694 | } else if (colorNum > 256) { 1695 | colorNum = 256; 1696 | } 1697 | this.text = colorNum; 1698 | parent.colorsLast = colorNum; 1699 | }; 1700 | 1701 | // dither 1702 | row = parent.add("group"); 1703 | var ditherLabel = row.add("statictext", undefined, "Dither:"); 1704 | ditherLabel.preferredSize = [LABEL_WIDTH, ROW_HEIGHT]; 1705 | parent.dither = row.add("dropdownlist", undefined, [ 1706 | "None", 1707 | "Diffusion", 1708 | "Pattern", 1709 | "Noise" 1710 | ]); 1711 | parent.dither.selection = 0; 1712 | 1713 | // dither amount 1714 | var ditherAmountGroup = row.add("group"); 1715 | parent.ditherAmount = ditherAmountGroup.add("slider", undefined, 100, 0, 100); 1716 | var ditherAmountValue = ditherAmountGroup.add("statictext", undefined, "100%"); 1717 | ditherAmountGroup.enabled = false; 1718 | 1719 | parent.ditherAmount.onChanging = function() { 1720 | this.value = Math.round(this.value); 1721 | ditherAmountValue.text = "" + this.value + "%"; 1722 | }; 1723 | 1724 | parent.dither.onChange = function() { 1725 | ditherAmountGroup.enabled = (this.selection == 1); 1726 | parent.ditherAmount.notify("onChanging"); 1727 | }; 1728 | 1729 | // interlaced 1730 | parent.interlaced = parent.add("checkbox", undefined, "Interlaced"); 1731 | 1732 | // transparency 1733 | var transparencyPanel = parent.add("panel", undefined, "Transparency:"); 1734 | transparencyPanel.orientation = "column"; 1735 | transparencyPanel.alignChildren = "left"; 1736 | parent.transparency = transparencyPanel.add("checkbox", undefined, "Enabled"); 1737 | parent.transparency.value = true; 1738 | 1739 | parent.transparency.onClick = function() { 1740 | matteRow.enabled = !this.value; 1741 | tdRow.enabled = this.value; 1742 | }; 1743 | 1744 | // matte 1745 | var matteRow = transparencyPanel.add("group"); 1746 | var matteLabel = matteRow.add("statictext", undefined, "Matte:"); 1747 | matteLabel.preferredSize = [LABEL_WIDTH + 8, ROW_HEIGHT]; 1748 | parent.matte = matteRow.add("dropdownlist", undefined, ["White", "Black", "Gray", "-", "Background", "Foreground"]); 1749 | parent.matte.selection = 0; 1750 | matteRow.enabled = false; 1751 | 1752 | // transparency dither 1753 | var tdRow = transparencyPanel.add("group"); 1754 | var transDitherLabel = tdRow.add("statictext", undefined, "Transparency dither:"); 1755 | transDitherLabel.preferredSize = [LABEL_WIDTH + 8, ROW_HEIGHT]; 1756 | parent.transparencyDither = tdRow.add("dropdownlist", undefined, [ 1757 | "None", 1758 | "Diffusion", 1759 | "Pattern", 1760 | "Noise" 1761 | ]); 1762 | parent.transparencyDither.selection = 0; 1763 | 1764 | parent.transparencyDither.onChange = function() { 1765 | transDitherAmountGroup.enabled = (this.selection == 1); 1766 | parent.transparencyDitherAmount.notify("onChanging"); 1767 | }; 1768 | 1769 | // transparency dither amount 1770 | var transDitherAmountGroup = tdRow.add("group"); 1771 | parent.transparencyDitherAmount = transDitherAmountGroup.add("slider", undefined, 100, 0, 100); 1772 | var transDitherAmountValue = transDitherAmountGroup.add("statictext", undefined, "100%"); 1773 | transDitherAmountGroup.enabled = false; 1774 | 1775 | parent.transparencyDitherAmount.onChanging = function() { 1776 | this.value = Math.round(this.value); 1777 | transDitherAmountValue.text = "" + this.value + "%"; 1778 | }; 1779 | }, 1780 | 1781 | // Reaction to dialog confirmation 1782 | onDialogSelect: function(parent) { 1783 | prefs.format = "PNG-8"; 1784 | prefs.fileExtension = ".png"; 1785 | 1786 | var colorReductionType = [ 1787 | ColorReductionType.PERCEPTUAL, 1788 | ColorReductionType.SELECTIVE, 1789 | ColorReductionType.ADAPTIVE, 1790 | ColorReductionType.RESTRICTIVE, 1791 | null, 1792 | ColorReductionType.BLACKWHITE, 1793 | ColorReductionType.GRAYSCALE, 1794 | ColorReductionType.MACINTOSH, 1795 | ColorReductionType.WINDOWS 1796 | ]; 1797 | var ditherType = [ 1798 | Dither.NONE, 1799 | Dither.DIFFUSION, 1800 | Dither.PATTERN, 1801 | Dither.NOISE 1802 | ]; 1803 | var WHITE = new RGBColor(); 1804 | WHITE.red = 255; 1805 | WHITE.green = 255; 1806 | WHITE.blue = 255; 1807 | var BLACK = new RGBColor(); 1808 | BLACK.red = 0; 1809 | BLACK.green = 0; 1810 | BLACK.blue = 0; 1811 | var GRAY = new RGBColor(); 1812 | GRAY.red = 127; 1813 | GRAY.green = 127; 1814 | GRAY.blue = 127; 1815 | var matteColors = [WHITE, BLACK, GRAY, BLACK, app.backgroundColor.rgb, app.foregroundColor.rgb]; 1816 | 1817 | prefs.formatArgs = new ExportOptionsSaveForWeb(); 1818 | with(prefs.formatArgs) { 1819 | format = SaveDocumentType.PNG; 1820 | PNG8 = true; 1821 | colorReduction = colorReductionType[parent.colorReduction.selection.index]; 1822 | colors = parseInt(parent.colors.text, 10); 1823 | dither = ditherType[parent.dither.selection.index]; 1824 | if (dither == Dither.DIFFUSION) { 1825 | ditherAmount = parent.ditherAmount.value; 1826 | } 1827 | interlaced = parent.interlaced.value; 1828 | transparency = parent.transparency.value; 1829 | matteColor = matteColors[parent.matte.selection.index]; 1830 | if (transparency) { 1831 | transparencyDither = ditherType[parent.transparencyDither.selection.index]; 1832 | if (transparencyDither == Dither.DIFFUSION) { 1833 | transparencyAmount = parent.transparencyDitherAmount.value; 1834 | } 1835 | } 1836 | } 1837 | }, 1838 | 1839 | settingsKeys: { 1840 | colorReduction: app.stringIDToTypeID("png8ColorReduction"), 1841 | numberOfColors: app.stringIDToTypeID("png8NumberOfColors"), 1842 | dither: app.stringIDToTypeID("png8Dither"), 1843 | ditherAmount: app.stringIDToTypeID("png8DitherAmount"), 1844 | interlaced: app.stringIDToTypeID("png8Interlaced"), 1845 | transparency: app.stringIDToTypeID("png8Transparency"), 1846 | matte: app.stringIDToTypeID("png8Matte"), 1847 | transparencyDither: app.stringIDToTypeID("png8TransparencyDither"), 1848 | transparencyDitherAmount: app.stringIDToTypeID("png8TransparencyDitherAmount") 1849 | }, 1850 | 1851 | // Save settings into an ActionDescriptor 1852 | packSettings: function(desc, formatOptRoot) { 1853 | desc.putInteger(this.settingsKeys.colorReduction, formatOptRoot.colorReduction.selection.index); 1854 | desc.putString(this.settingsKeys.numberOfColors, formatOptRoot.colors.text); 1855 | desc.putInteger(this.settingsKeys.dither, formatOptRoot.dither.selection.index); 1856 | desc.putInteger(this.settingsKeys.ditherAmount, formatOptRoot.ditherAmount.value); 1857 | desc.putBoolean(this.settingsKeys.interlaced, formatOptRoot.interlaced.value); 1858 | desc.putBoolean(this.settingsKeys.transparency, formatOptRoot.transparency.value); 1859 | desc.putInteger(this.settingsKeys.matte, formatOptRoot.matte.selection.index); 1860 | desc.putInteger(this.settingsKeys.transparencyDither, formatOptRoot.transparencyDither.selection.index); 1861 | desc.putInteger(this.settingsKeys.transparencyDitherAmount, formatOptRoot.transparencyDitherAmount.value); 1862 | }, 1863 | 1864 | // Get settings from an ActionDescriptor 1865 | unpackSettings: function(desc) { 1866 | return { 1867 | colorReduction: desc.getInteger(this.settingsKeys.colorReduction), 1868 | numberOfColors: desc.getString(this.settingsKeys.numberOfColors), 1869 | dither: desc.getInteger(this.settingsKeys.dither), 1870 | ditherAmount: desc.getInteger(this.settingsKeys.ditherAmount), 1871 | interlaced: desc.getBoolean(this.settingsKeys.interlaced), 1872 | transparency: desc.getBoolean(this.settingsKeys.transparency), 1873 | matte: desc.getInteger(this.settingsKeys.matte), 1874 | transparencyDither: desc.getInteger(this.settingsKeys.transparencyDither), 1875 | transparencyDitherAmount: desc.getInteger(this.settingsKeys.transparencyDitherAmount) 1876 | }; 1877 | }, 1878 | 1879 | // Apply settings to dialog GUI 1880 | applySettings: function(settings, formatOptRoot) { 1881 | var formatSettings = settings.format[this.type]; 1882 | formatOptRoot.colorReduction.selection = formatSettings.colorReduction; 1883 | formatOptRoot.colors.text = formatSettings.numberOfColors; 1884 | formatOptRoot.dither.selection = formatSettings.dither; 1885 | //formatOptRoot.dither.notify("onChange"); 1886 | formatOptRoot.ditherAmount.value = formatSettings.ditherAmount; 1887 | formatOptRoot.interlaced.value = formatSettings.interlaced; 1888 | formatOptRoot.matte.selection = formatSettings.matte; 1889 | formatOptRoot.transparencyDither.selection = formatSettings.transparencyDither; 1890 | //formatOptRoot.transparencyDither.notify("onChange"); 1891 | formatOptRoot.transparencyDitherAmount.value = formatSettings.transparencyDitherAmount; 1892 | 1893 | formatOptRoot.colors.notify(); 1894 | if (formatOptRoot.transparency.value != formatSettings.transparency) { 1895 | formatOptRoot.transparency.notify(); 1896 | } 1897 | formatOptRoot.ditherAmount.notify("onChanging"); 1898 | formatOptRoot.transparencyDitherAmount.notify("onChanging"); 1899 | } 1900 | }; 1901 | } 1902 | 1903 | function getFormatOptsBMP() { 1904 | return { 1905 | type: "BMP", 1906 | 1907 | // Dialog GUI 1908 | dialogParams: function(parent) { 1909 | // bit depth 1910 | var depth = parent.add("group"); 1911 | depth.add("statictext", undefined, "Depth:"); 1912 | var depthLabels = [ 1913 | "32 bit", 1914 | "24 bit", 1915 | "RGB 565 (16 bit)", 1916 | "ARGB 1555 (16 bit)", 1917 | "ARGB 4444 (16 bit)" 1918 | ]; 1919 | parent.depth = depth.add("dropdownlist", undefined, depthLabels); 1920 | parent.depth.selection = 0; 1921 | 1922 | // alpha 1923 | parent.alpha = parent.add("checkbox", undefined, "With alpha channel"); 1924 | parent.alpha.value = true; 1925 | 1926 | // RLE 1927 | parent.rle = parent.add("checkbox", undefined, "RLE compression"); 1928 | parent.rle.value = true; 1929 | 1930 | // flip row order 1931 | parent.flipRowOrder = parent.add("checkbox", undefined, "Flip row order"); 1932 | parent.flipRowOrder.value = false; 1933 | }, 1934 | 1935 | // Reaction to dialog confirmation 1936 | onDialogSelect: function(parent) { 1937 | prefs.format = "BMP"; 1938 | prefs.fileExtension = ".bmp"; 1939 | prefs.formatArgs = new BMPSaveOptions(); 1940 | prefs.formatArgs.osType = OperatingSystem.WINDOWS; 1941 | prefs.formatArgs.alphaChannels = parent.alpha.value; 1942 | prefs.formatArgs.rleCompression = parent.rle.value; 1943 | prefs.formatArgs.flipRowOrder = parent.flipRowOrder.value; 1944 | var resolution_enum = [ 1945 | BMPDepthType.THIRTYTWO, 1946 | BMPDepthType.TWENTYFOUR, 1947 | BMPDepthType.BMP_R5G6B5, 1948 | BMPDepthType.BMP_A1R5G5B5, 1949 | BMPDepthType.BMP_A4R4G4B4 1950 | ]; 1951 | prefs.formatArgs.depth = resolution_enum[parent.depth.selection.index]; 1952 | }, 1953 | 1954 | settingsKeys: { 1955 | depth: app.stringIDToTypeID("bmpDepth"), 1956 | alpha: app.stringIDToTypeID("bmpAlpha"), 1957 | rle: app.stringIDToTypeID("bmpRle"), 1958 | flipRow: app.stringIDToTypeID("bmpFlipRow") 1959 | }, 1960 | 1961 | // Save settings into an ActionDescriptor 1962 | packSettings: function(desc, formatOptRoot) { 1963 | desc.putBoolean(this.settingsKeys.alpha, formatOptRoot.alpha.value); 1964 | desc.putInteger(this.settingsKeys.depth, formatOptRoot.depth.selection.index); 1965 | desc.putBoolean(this.settingsKeys.rle, formatOptRoot.rle.value); 1966 | desc.putBoolean(this.settingsKeys.flipRow, formatOptRoot.flipRowOrder.value); 1967 | }, 1968 | 1969 | // Get settings from an ActionDescriptor 1970 | unpackSettings: function(desc) { 1971 | return { 1972 | depth: desc.getInteger(this.settingsKeys.depth), 1973 | alpha: desc.getBoolean(this.settingsKeys.alpha), 1974 | rle: desc.getBoolean(this.settingsKeys.rle), 1975 | flipRow: desc.getBoolean(this.settingsKeys.flipRow) 1976 | }; 1977 | }, 1978 | 1979 | // Apply settings to dialog GUI 1980 | applySettings: function(settings, formatOptRoot) { 1981 | var formatSettings = settings.format[this.type]; 1982 | formatOptRoot.alpha.value = formatSettings.alpha; 1983 | formatOptRoot.depth.selection = formatSettings.depth; 1984 | formatOptRoot.rle.value = formatSettings.rle; 1985 | formatOptRoot.flipRowOrder.value = formatSettings.flipRow; 1986 | } 1987 | }; 1988 | } 1989 | 1990 | 1991 | // 1992 | // Bootstrapper (version support, getting additional environment settings, error handling...) 1993 | // 1994 | 1995 | function bootstrap() { 1996 | function showError(err) { 1997 | alert(err + ': on line ' + err.line, 'Script Error', true); 1998 | } 1999 | 2000 | // initialisation of class methods 2001 | defineProfilerMethods(); 2002 | 2003 | // check if there's a document open 2004 | try { 2005 | var doc = app.activeDocument; // this actually triggers the exception 2006 | if (!doc) { // this is just for sure if it ever behaves differently in other versions 2007 | throw new Error(); 2008 | } 2009 | } catch (e) { 2010 | alert("No document is open! Nothing to export.", "Error", true); 2011 | return "cancel"; 2012 | } 2013 | 2014 | try { 2015 | // setup the environment 2016 | 2017 | env = new Object(); 2018 | 2019 | env.version = parseInt(app.version, 10); 2020 | 2021 | if (env.version < 9) { 2022 | alert("Photoshop versions before CS2 are not supported!", "Error", true); 2023 | return "cancel"; 2024 | } 2025 | 2026 | env.cs3OrHigher = (env.version >= 10); 2027 | 2028 | // get script's file name 2029 | if (env.cs3OrHigher) { 2030 | env.scriptFileName = $.fileName; 2031 | } else { 2032 | try { 2033 | //throw new Error(); // doesn't provide the file name, at least in CS2 2034 | var illegal = RUNTIME_ERROR; 2035 | } catch (e) { 2036 | env.scriptFileName = e.fileName; 2037 | } 2038 | } 2039 | 2040 | env.scriptFileDirectory = (new File(env.scriptFileName)).parent; 2041 | 2042 | // run the script itself 2043 | if (env.cs3OrHigher) { 2044 | // suspend history for CS3 or higher 2045 | app.activeDocument.suspendHistory('Export Layers To Files', 'main()'); 2046 | } else { 2047 | main(); 2048 | } 2049 | 2050 | if (env.documentCopy) { 2051 | env.documentCopy.close(SaveOptions.DONOTSAVECHANGES); 2052 | } 2053 | } catch (e) { 2054 | // report errors unless the user cancelled 2055 | if (e.number != 8007) showError(e); 2056 | if (env.documentCopy) { 2057 | env.documentCopy.close(SaveOptions.DONOTSAVECHANGES); 2058 | } 2059 | return "cancel"; 2060 | } 2061 | } 2062 | 2063 | // 2064 | // ActionManager mud 2065 | // 2066 | 2067 | // Faster layer collection: 2068 | // https://forums.adobe.com/message/2666611 2069 | 2070 | function collectLayersAM(progressBarWindow) { 2071 | var layers = [], 2072 | visibleLayers = [], 2073 | selectedLayers = [], 2074 | groups = []; 2075 | var layerCount = 0; 2076 | 2077 | var ref = null; 2078 | var desc = null; 2079 | 2080 | var idOrdn = app.charIDToTypeID("Ordn"); 2081 | 2082 | // Get layer count reported by the active Document object - it never includes the background. 2083 | ref = new ActionReference(); 2084 | ref.putEnumerated(app.charIDToTypeID("Dcmn"), app.charIDToTypeID("Ordn"), app.charIDToTypeID("Trgt")); 2085 | desc = app.executeActionGet(ref); 2086 | layerCount = desc.getInteger(app.charIDToTypeID("NmbL")); 2087 | 2088 | if (layerCount == 0) { 2089 | // This is a flattened image that contains only the background (which is always visible). 2090 | var bg = app.activeDocument.backgroundLayer; 2091 | var layer = { layer: bg, parent: null }; 2092 | layers.push(layer); 2093 | visibleLayers.push(layer); 2094 | } else { 2095 | // There are more layers that may or may not contain a background. The background is always at 0; 2096 | // other layers are indexed from 1. 2097 | 2098 | var idLyr = app.charIDToTypeID("Lyr "); 2099 | var idLayerSection = app.stringIDToTypeID("layerSection"); 2100 | var idVsbl = app.charIDToTypeID("Vsbl"); 2101 | var idNull = app.charIDToTypeID("null"); 2102 | var idSlct = app.charIDToTypeID("slct"); 2103 | var idMkVs = app.charIDToTypeID("MkVs"); 2104 | 2105 | var FEW_LAYERS = 10; 2106 | 2107 | // newer PS's freeze or crash on Mac OS X Yosemite 2108 | //if (layerCount <= FEW_LAYERS) { 2109 | // don't show the progress bar UI for only a few layers 2110 | //progressBarWindow = null; 2111 | //} 2112 | 2113 | if (progressBarWindow) { 2114 | // The layer count is actually + 1 if there's a background present, but it should be no biggie. 2115 | showProgressBar(progressBarWindow, "Collecting layers... Might take up to several seconds.", (layerCount + FEW_LAYERS) / FEW_LAYERS); 2116 | } 2117 | 2118 | // Query current selection. 2119 | ref = new ActionReference(); 2120 | ref.putEnumerated(idLyr, idOrdn, app.charIDToTypeID("Trgt")); 2121 | var selectionDesc = app.executeActionGet(ref); 2122 | var selectionIdx = selectionDesc.getInteger(app.charIDToTypeID("ItmI")); 2123 | 2124 | try { 2125 | // Collect normal layers. 2126 | var visibleInGroup = [true]; 2127 | var layerVisible; 2128 | var currentGroup = null; 2129 | var layerSection; 2130 | var selected = 0; 2131 | for (var i = layerCount; i >= 1; --i) { 2132 | // check if it's an art layer (not a group) that can be selected 2133 | ref = new ActionReference(); 2134 | ref.putIndex(idLyr, i); 2135 | desc = app.executeActionGet(ref); 2136 | layerVisible = desc.getBoolean(idVsbl); 2137 | layerSection = app.typeIDToStringID(desc.getEnumerationValue(idLayerSection)); 2138 | if ((layerSection == "layerSectionContent") || 2139 | (layerSection == "layerSectionStart")) { 2140 | // select the layer and then retrieve it via Document.activeLayer 2141 | desc.clear(); 2142 | desc.putReference(idNull, ref); 2143 | desc.putBoolean(idMkVs, false); 2144 | app.executeAction(idSlct, desc, DialogModes.NO); 2145 | 2146 | var activeLayer = app.activeDocument.activeLayer; 2147 | 2148 | if (layerSection == "layerSectionContent") { 2149 | if (!isAdjustmentLayer(activeLayer)) { 2150 | var layer = { layer: activeLayer, parent: currentGroup }; 2151 | layers.push(layer); 2152 | if (layerVisible && visibleInGroup[visibleInGroup.length - 1]) { 2153 | visibleLayers.push(layer); 2154 | } 2155 | if (selected > 0) { 2156 | selectedLayers.push(layer); 2157 | } 2158 | if (currentGroup) { 2159 | currentGroup.children.push(layer); 2160 | } 2161 | } 2162 | } else { 2163 | var group = { layer: activeLayer, parent: currentGroup, children: [] }; 2164 | group.visible = (layerVisible && visibleInGroup[visibleInGroup.length - 1]); 2165 | if (group.parent == null) { 2166 | groups.push(group); 2167 | } else { 2168 | group.parent.children.push(group); 2169 | } 2170 | currentGroup = group; 2171 | visibleInGroup.push(group.visible); 2172 | // Only check for selected groups. In CS2, 1 and only 1 layer/group is always selected (active). 2173 | // It is useless to export just 1 art layer, so only layer groups (sets) are supported. 2174 | if ((selectionIdx == i) || (selected > 0)) { 2175 | selected++; 2176 | group.selected = true; 2177 | } 2178 | } 2179 | } else if (layerSection == "layerSectionEnd") { 2180 | currentGroup = currentGroup.parent; 2181 | visibleInGroup.pop(); 2182 | if (selected > 0) { 2183 | selected--; 2184 | } 2185 | } 2186 | 2187 | if (progressBarWindow && ((i % FEW_LAYERS == 0) || (i == layerCount))) { 2188 | updateProgressBar(progressBarWindow); 2189 | repaintProgressBar(progressBarWindow); 2190 | if (userCancelled) { 2191 | throw new Error("cancel"); 2192 | } 2193 | } 2194 | } 2195 | 2196 | // Collect the background. 2197 | ref = new ActionReference(); 2198 | ref.putIndex(idLyr, 0); 2199 | try { 2200 | desc = app.executeActionGet(ref); 2201 | var bg = app.activeDocument.backgroundLayer; 2202 | var layer = { layer: bg, parent: null }; 2203 | layers.push(layer); 2204 | if (bg.visible) { 2205 | visibleLayers.push(layer); 2206 | } 2207 | 2208 | if (progressBarWindow) { 2209 | updateProgressBar(progressBarWindow); 2210 | repaintProgressBar(progressBarWindow); 2211 | } 2212 | } catch (e) { 2213 | // no background, move on 2214 | } 2215 | } catch (e) { 2216 | if (e.message != "cancel") throw e; 2217 | } 2218 | 2219 | // restore selection (unfortunately CS2 doesn't support multiselection, so only the topmost layer is re-selected) 2220 | /*desc.clear(); 2221 | ref = new ActionReference(); 2222 | var totalLayerCount = selectionDesc.getInteger(app.charIDToTypeID("Cnt ")); 2223 | ref.putIndex(idLyr, selectionDesc.getInteger(app.charIDToTypeID("ItmI")) - (totalLayerCount - layerCount)); 2224 | desc.putReference(idNull, ref); 2225 | desc.putBoolean(idMkVs, false); 2226 | app.executeAction(idSlct, desc, DialogModes.NO);*/ 2227 | 2228 | if (progressBarWindow) { 2229 | progressBarWindow.hide(); 2230 | } 2231 | } 2232 | 2233 | return { layers: layers, visibleLayers: visibleLayers, selectedLayers: selectedLayers, groups: groups }; 2234 | } 2235 | 2236 | function countLayersAM(progressBarWindow) { 2237 | var layerCount = 0; 2238 | var preciseLayerCount = 0; 2239 | var visLayerCount = 0; 2240 | var selLayerCount = 0; 2241 | 2242 | var ref = null; 2243 | var desc = null; 2244 | 2245 | var idOrdn = app.charIDToTypeID("Ordn"); 2246 | var idLyr = app.charIDToTypeID("Lyr "); 2247 | 2248 | // Get layer count reported by the active Document object - it never includes the background. 2249 | ref = new ActionReference(); 2250 | ref.putEnumerated(app.charIDToTypeID("Dcmn"), app.charIDToTypeID("Ordn"), app.charIDToTypeID("Trgt")); 2251 | desc = app.executeActionGet(ref); 2252 | layerCount = desc.getInteger(app.charIDToTypeID("NmbL")); 2253 | 2254 | // Query current selection. 2255 | ref = new ActionReference(); 2256 | ref.putEnumerated(idLyr, idOrdn, app.charIDToTypeID("Trgt")); 2257 | var selectionDesc = app.executeActionGet(ref); 2258 | // Something is always selected even if nothing is selected in GUI. 2259 | var selectionIdx = selectionDesc.getInteger(app.charIDToTypeID("ItmI")); 2260 | 2261 | if (layerCount == 0) { 2262 | // This is a flattened image that contains only the background (which is always visible). 2263 | preciseLayerCount = 1; 2264 | visLayerCount = 1; 2265 | } else { 2266 | // There are more layers that may or may not contain a background. The background is always at 0; 2267 | // other layers are indexed from 1. 2268 | 2269 | var idLayerSection = app.stringIDToTypeID("layerSection"); 2270 | var idVsbl = app.charIDToTypeID("Vsbl"); 2271 | var idNull = app.charIDToTypeID("null"); 2272 | var idSlct = app.charIDToTypeID("slct"); 2273 | var idMkVs = app.charIDToTypeID("MkVs"); 2274 | 2275 | var FEW_LAYERS = 10; 2276 | 2277 | // newer PS's freeze or crash on Mac OS X Yosemite 2278 | //if (layerCount <= FEW_LAYERS) { 2279 | // don't show the progress bar UI for only a few layers 2280 | //progressBarWindow = null; 2281 | //} 2282 | 2283 | if (progressBarWindow) { 2284 | // The layer count is actually + 1 if there's a background present, but it should be no biggie. 2285 | showProgressBar(progressBarWindow, "Counting layers... Might take up to several seconds.", (layerCount + FEW_LAYERS) / FEW_LAYERS); 2286 | } 2287 | 2288 | try { 2289 | // Collect normal layers. 2290 | var visibleInGroup = [true]; 2291 | var layerVisible; 2292 | var layerSection; 2293 | var selected = 0; 2294 | for (var i = layerCount; i >= 1; --i) { 2295 | // check if it's an art layer (not a group) that can be selected 2296 | ref = new ActionReference(); 2297 | ref.putIndex(idLyr, i); 2298 | desc = app.executeActionGet(ref); 2299 | layerVisible = desc.getBoolean(idVsbl); 2300 | layerSection = app.typeIDToStringID(desc.getEnumerationValue(idLayerSection)); 2301 | if (layerSection == "layerSectionContent") { 2302 | preciseLayerCount++; 2303 | if (layerVisible && visibleInGroup[visibleInGroup.length - 1]) { 2304 | visLayerCount++; 2305 | } 2306 | if (selected > 0) { 2307 | selLayerCount++; 2308 | } 2309 | } else if (layerSection == "layerSectionStart") { 2310 | visibleInGroup.push(layerVisible && visibleInGroup[visibleInGroup.length - 1]); 2311 | // Only check for selected groups. In CS2, 1 and only 1 layer/group is always selected (active). 2312 | // It is useless to export just 1 art layer, so only layer groups (sets) are supported. 2313 | if ((selectionIdx == i) || (selected > 0)) { 2314 | selected++; 2315 | } 2316 | } else if (layerSection == "layerSectionEnd") { 2317 | visibleInGroup.pop(); 2318 | if (selected > 0) { 2319 | selected--; 2320 | } 2321 | } 2322 | 2323 | if (progressBarWindow && ((i % FEW_LAYERS == 0) || (i == layerCount))) { 2324 | updateProgressBar(progressBarWindow); 2325 | repaintProgressBar(progressBarWindow); 2326 | if (userCancelled) { 2327 | throw new Error("cancel"); 2328 | } 2329 | } 2330 | } 2331 | 2332 | // Collect the background. 2333 | try { 2334 | var bg = app.activeDocument.backgroundLayer; 2335 | preciseLayerCount++; 2336 | if (bg.visible) { 2337 | visLayerCount++; 2338 | } 2339 | 2340 | if (progressBarWindow) { 2341 | updateProgressBar(progressBarWindow); 2342 | repaintProgressBar(progressBarWindow); 2343 | } 2344 | } catch (e) { 2345 | // no background, move on 2346 | } 2347 | } catch (e) { 2348 | if (e.message != "cancel") throw e; 2349 | } 2350 | 2351 | if (progressBarWindow) { 2352 | progressBarWindow.hide(); 2353 | } 2354 | } 2355 | 2356 | return { layerCount: preciseLayerCount, visibleLayerCount: visLayerCount, selectedLayerCount: selLayerCount }; 2357 | } 2358 | 2359 | function exportPng24AM(fileName, options) { 2360 | var desc = new ActionDescriptor(), 2361 | desc2 = new ActionDescriptor(); 2362 | desc2.putEnumerated(app.charIDToTypeID("Op "), app.charIDToTypeID("SWOp"), app.charIDToTypeID("OpSa")); 2363 | desc2.putEnumerated(app.charIDToTypeID("Fmt "), app.charIDToTypeID("IRFm"), app.charIDToTypeID("PN24")); 2364 | desc2.putBoolean(app.charIDToTypeID("Intr"), options.interlaced); 2365 | desc2.putBoolean(app.charIDToTypeID("Trns"), options.transparency); 2366 | desc2.putBoolean(app.charIDToTypeID("Mtt "), true); 2367 | desc2.putInteger(app.charIDToTypeID("MttR"), options.matteColor.red); 2368 | desc2.putInteger(app.charIDToTypeID("MttG"), options.matteColor.green); 2369 | desc2.putInteger(app.charIDToTypeID("MttB"), options.matteColor.blue); 2370 | desc2.putBoolean(app.charIDToTypeID("SHTM"), false); 2371 | desc2.putBoolean(app.charIDToTypeID("SImg"), true); 2372 | desc2.putBoolean(app.charIDToTypeID("SSSO"), false); 2373 | desc2.putList(app.charIDToTypeID("SSLt"), new ActionList()); 2374 | desc2.putBoolean(app.charIDToTypeID("DIDr"), false); 2375 | desc2.putPath(app.charIDToTypeID("In "), new File(fileName)); 2376 | desc.putObject(app.charIDToTypeID("Usng"), app.stringIDToTypeID("SaveForWeb"), desc2); 2377 | app.executeAction(app.charIDToTypeID("Expr"), desc, DialogModes.NO); 2378 | } 2379 | 2380 | function exportPng8AM(fileName, options) { 2381 | var id5 = app.charIDToTypeID("Expr"); 2382 | var desc3 = new ActionDescriptor(); 2383 | var id6 = app.charIDToTypeID("Usng"); 2384 | var desc4 = new ActionDescriptor(); 2385 | var id7 = app.charIDToTypeID("Op "); 2386 | var id8 = app.charIDToTypeID("SWOp"); 2387 | var id9 = app.charIDToTypeID("OpSa"); 2388 | desc4.putEnumerated(id7, id8, id9); 2389 | var id10 = app.charIDToTypeID("Fmt "); 2390 | var id11 = app.charIDToTypeID("IRFm"); 2391 | var id12 = app.charIDToTypeID("PNG8"); 2392 | desc4.putEnumerated(id10, id11, id12); 2393 | var id13 = app.charIDToTypeID("Intr"); //Interlaced 2394 | desc4.putBoolean(id13, options.interlaced); 2395 | var id14 = app.charIDToTypeID("RedA"); 2396 | var id15 = app.charIDToTypeID("IRRd"); 2397 | //Algorithm 2398 | var id16; 2399 | switch (options.colorReduction) { 2400 | 2401 | case ColorReductionType.PERCEPTUAL: 2402 | id16 = app.charIDToTypeID("Prcp"); 2403 | break; 2404 | 2405 | case ColorReductionType.SELECTIVE: 2406 | id16 = app.charIDToTypeID("Sltv"); 2407 | break; 2408 | 2409 | case ColorReductionType.ADAPTIVE: 2410 | id16 = app.charIDToTypeID("Adpt"); 2411 | break; 2412 | 2413 | case ColorReductionType.RESTRICTIVE: 2414 | id16 = app.charIDToTypeID("Web "); 2415 | break; 2416 | 2417 | // CUSTOM not supported 2418 | 2419 | case ColorReductionType.BLACKWHITE: 2420 | case ColorReductionType.GRAYSCALE: 2421 | case ColorReductionType.MACINTOSH: 2422 | case ColorReductionType.WINDOWS: 2423 | id16 = app.charIDToTypeID("FlBs"); 2424 | break; 2425 | 2426 | default: 2427 | throw new Error("Unknown color reduction algorithm. Cannot export PNG-8!"); 2428 | } 2429 | desc4.putEnumerated(id14, id15, id16); 2430 | var id361 = app.charIDToTypeID("FBPl"); 2431 | switch (options.colorReduction) { 2432 | 2433 | case ColorReductionType.BLACKWHITE: 2434 | desc4.putString(id361, "Black & White"); 2435 | break; 2436 | 2437 | case ColorReductionType.GRAYSCALE: 2438 | desc4.putString(id361, "Grayscale"); 2439 | break; 2440 | 2441 | case ColorReductionType.MACINTOSH: 2442 | desc4.putString(id361, "Mac OS"); 2443 | break; 2444 | 2445 | case ColorReductionType.WINDOWS: 2446 | desc4.putString(id361, "Windows"); 2447 | break; 2448 | } 2449 | var id17 = app.charIDToTypeID("RChT"); 2450 | desc4.putBoolean(id17, false); 2451 | var id18 = app.charIDToTypeID("RChV"); 2452 | desc4.putBoolean(id18, false); 2453 | var id19 = app.charIDToTypeID("AuRd"); 2454 | desc4.putBoolean(id19, false); 2455 | var id20 = app.charIDToTypeID("NCol"); //NO. Of Colors 2456 | desc4.putInteger(id20, options.colors); 2457 | var id21 = app.charIDToTypeID("Dthr"); //Dither 2458 | var id22 = app.charIDToTypeID("IRDt"); 2459 | //Dither type 2460 | var id23; 2461 | switch (options.dither) { 2462 | 2463 | case Dither.NONE: 2464 | id23 = app.charIDToTypeID("None"); 2465 | break; 2466 | 2467 | case Dither.DIFFUSION: 2468 | id23 = app.charIDToTypeID("Dfsn"); 2469 | break; 2470 | 2471 | case Dither.PATTERN: 2472 | id23 = app.charIDToTypeID("Ptrn"); 2473 | break; 2474 | 2475 | case Dither.NOISE: 2476 | id23 = app.charIDToTypeID("BNoi"); 2477 | break; 2478 | 2479 | default: 2480 | throw new Error("Unknown dither type. Cannot export PNG-8!"); 2481 | } 2482 | desc4.putEnumerated(id21, id22, id23); 2483 | var id24 = app.charIDToTypeID("DthA"); 2484 | desc4.putInteger(id24, options.ditherAmount); 2485 | var id25 = app.charIDToTypeID("DChS"); 2486 | desc4.putInteger(id25, 0); 2487 | var id26 = app.charIDToTypeID("DCUI"); 2488 | desc4.putInteger(id26, 0); 2489 | var id27 = app.charIDToTypeID("DChT"); 2490 | desc4.putBoolean(id27, false); 2491 | var id28 = app.charIDToTypeID("DChV"); 2492 | desc4.putBoolean(id28, false); 2493 | var id29 = app.charIDToTypeID("WebS"); 2494 | desc4.putInteger(id29, 0); 2495 | var id30 = app.charIDToTypeID("TDth"); //transparency dither 2496 | var id31 = app.charIDToTypeID("IRDt"); 2497 | var id32; 2498 | switch (options.transparencyDither) { 2499 | 2500 | case Dither.NONE: 2501 | id32 = app.charIDToTypeID("None"); 2502 | break; 2503 | 2504 | case Dither.DIFFUSION: 2505 | id32 = app.charIDToTypeID("Dfsn"); 2506 | break; 2507 | 2508 | case Dither.PATTERN: 2509 | id32 = app.charIDToTypeID("Ptrn"); 2510 | break; 2511 | 2512 | case Dither.NOISE: 2513 | id32 = app.charIDToTypeID("BNoi"); 2514 | break; 2515 | 2516 | default: 2517 | throw new Error("Unknown transparency dither algorithm. Cannot export PNG-8!"); 2518 | } 2519 | desc4.putEnumerated(id30, id31, id32); 2520 | var id33 = app.charIDToTypeID("TDtA"); 2521 | desc4.putInteger(id33, options.transparencyAmount); 2522 | var id34 = app.charIDToTypeID("Trns"); //Transparency 2523 | desc4.putBoolean(id34, options.transparency); 2524 | var id35 = app.charIDToTypeID("Mtt "); 2525 | desc4.putBoolean(id35, true); //matte 2526 | var id36 = app.charIDToTypeID("MttR"); //matte color 2527 | desc4.putInteger(id36, options.matteColor.red); 2528 | var id37 = app.charIDToTypeID("MttG"); 2529 | desc4.putInteger(id37, options.matteColor.green); 2530 | var id38 = app.charIDToTypeID("MttB"); 2531 | desc4.putInteger(id38, options.matteColor.blue); 2532 | var id39 = app.charIDToTypeID("SHTM"); 2533 | desc4.putBoolean(id39, false); 2534 | var id40 = app.charIDToTypeID("SImg"); 2535 | desc4.putBoolean(id40, true); 2536 | var id41 = app.charIDToTypeID("SSSO"); 2537 | desc4.putBoolean(id41, false); 2538 | var id42 = app.charIDToTypeID("SSLt"); 2539 | var list1 = new ActionList(); 2540 | desc4.putList(id42, list1); 2541 | var id43 = app.charIDToTypeID("DIDr"); 2542 | desc4.putBoolean(id43, false); 2543 | var id44 = app.charIDToTypeID("In "); 2544 | desc4.putPath(id44, new File(fileName)); 2545 | var id45 = app.stringIDToTypeID("SaveForWeb"); 2546 | desc3.putObject(id6, id45, desc4); 2547 | app.executeAction(id5, desc3, DialogModes.NO); 2548 | } 2549 | 2550 | // 2551 | // Utilities 2552 | // 2553 | 2554 | function padder(input, padLength) { 2555 | // pad the input with zeroes up to indicated length 2556 | var result = (new Array(padLength + 1 - input.toString().length)).join('0') + input; 2557 | return result; 2558 | } 2559 | 2560 | function makeValidFileName(fileName, replaceSpaces) { 2561 | var validName = fileName.replace(/^\s+|\s+$/gm, ''); // trim spaces 2562 | validName = validName.replace(/[\\\*\/\?:"\|<>]/g, ''); // remove characters not allowed in a file name 2563 | if (replaceSpaces) { 2564 | validName = validName.replace(/[ ]/g, prefs.delimiter); // replace spaces with chosen delimiter, since some programs still may have troubles with them 2565 | } 2566 | return validName; 2567 | } 2568 | 2569 | function formatString(text) { 2570 | var args = Array.prototype.slice.call(arguments, 1); 2571 | return text.replace(/\{(\d+)\}/g, function(match, number) { 2572 | return (typeof args[number] != 'undefined') ? args[number] : match; 2573 | }); 2574 | } 2575 | 2576 | var history; 2577 | 2578 | function storeHistory() { 2579 | history = app.activeDocument.activeHistoryState; 2580 | } 2581 | 2582 | function restoreHistory() { 2583 | app.activeDocument.activeHistoryState = history; 2584 | } 2585 | 2586 | function indexOf(array, element) { 2587 | var index = -1; 2588 | for (var i = 0; i < array.length; ++i) { 2589 | if (array[i] === element) { 2590 | index = i; 2591 | break; 2592 | } 2593 | } 2594 | 2595 | return index; 2596 | } 2597 | 2598 | function loadResource(file) { 2599 | var rsrcString; 2600 | if (!file.exists) { 2601 | alert("Resource file '" + file.name + "' for the export dialog is missing! Please, download the rest of the files that come with this script.", "Error", true); 2602 | return false; 2603 | } 2604 | try { 2605 | file.open("r"); 2606 | if (file.error) throw file.error; 2607 | rsrcString = file.read(); 2608 | if (file.error) throw file.error; 2609 | if (!file.close()) { 2610 | throw file.error; 2611 | } 2612 | } catch (error) { 2613 | alert("Failed to read the resource file '" + file.name + "'!\n\nReason: " + error + "\n\nPlease, check it's available for reading and redownload it in case it became corrupted.", "Error", true); 2614 | return false; 2615 | } 2616 | 2617 | return rsrcString; 2618 | } 2619 | 2620 | 2621 | function Profiler(enabled) { 2622 | this.enabled = enabled; 2623 | if (this.enabled) { 2624 | this.startTime = new Date(); 2625 | this.lastTime = this.startTime; 2626 | } 2627 | } 2628 | 2629 | function defineProfilerMethods() { 2630 | Profiler.prototype.getDuration = function(rememberAsLastCall, sinceLastCall) { 2631 | if (this.enabled) { 2632 | var currentTime = new Date(); 2633 | var lastTime = sinceLastCall ? this.lastTime : this.startTime; 2634 | if (rememberAsLastCall) { 2635 | this.lastTime = currentTime; 2636 | } 2637 | return new Date(currentTime.getTime() - lastTime.getTime()); 2638 | } 2639 | } 2640 | 2641 | Profiler.prototype.resetLastTime = function() { 2642 | this.lastTime = new Date(); 2643 | }; 2644 | 2645 | Profiler.prototype.format = function(duration) { 2646 | var output = padder(duration.getUTCHours(), 2) + ":"; 2647 | output += padder(duration.getUTCMinutes(), 2) + ":"; 2648 | output += padder(duration.getUTCSeconds(), 2) + "."; 2649 | output += padder(duration.getUTCMilliseconds(), 3); 2650 | return output; 2651 | }; 2652 | } 2653 | --------------------------------------------------------------------------------