├── 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 |
--------------------------------------------------------------------------------