├── settingsBox.png ├── README.md └── us-android-export.jsx /settingsBox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djavan-bertrand/export-to-android/HEAD/settingsBox.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Assets for Photoshop 2 | 3 | 4 | A Photoshop script for exporting assets for Android projects. 5 | 6 | The script works by duplicating the selected layer (or layergroup) to a new document, then scaling it to each of the 5 common Android sizes (XXXHDPI, XXHDPI, XHDPI, HDPI and MDPI) and then putting the files inside density related folders. 7 | 8 | You can specify some parameters for the generated files: 9 | 10 | * the name of the generated files. 11 | * the width/heigth of the mdpi generated file (other sizes will be related to this base size). 12 | * the generated file type (jpg or png). 13 | * the root folder where to store all density related folders (if no path is specified, resources will be saved in the same folder as your original resource). 14 | 15 | ![alt settingsBox](https://raw.githubusercontent.com/djavan-bertrand/export-to-android/master/settingsBox.png) 16 | 17 | This script is based on [this repo](https://github.com/UncorkedStudios/export-to-android). 18 | 19 | ## Installation 20 | 1. Download the script here 21 | 22 | 2. Move the .jsx file to your Photoshop scripts folder : 23 | * On Windows: C:\Program Files\Adobe\Adobe Photoshop CS6\Presets\Scripts (or C:\Program Files\Adobe\Adobe Photoshop CS6 (64 Bit)\Presets\Scripts if you're running a 64 bit version) 24 | * On Mac: /Applications/Adobe Photoshop …/Presets/Scripts/ 25 | 26 | -------------------------------------------------------------------------------- /us-android-export.jsx: -------------------------------------------------------------------------------- 1 | /*! 2 | * Android Assets for Photoshop 3 | * ============================= 4 | * 5 | * Version: 0.0.5 6 | * Author: Gaston Figueroa (Uncorked Studios) 7 | * Site: uncorkedstudios.com 8 | * Licensed under the MIT license 9 | */ 10 | 11 | 12 | // Photoshop variables 13 | var docRef, 14 | activeLayer, 15 | activeLayer2, 16 | mdpiWidth, 17 | mdpiHeight, 18 | folderPath, 19 | fileName, 20 | isPng; 21 | 22 | /****************************************/ 23 | /* Functions implementation */ 24 | /****************************************/ 25 | 26 | /** 27 | * Create a settings dialog window and return it 28 | * In this dialog window there will be inputs for : 29 | * file name, size, file type, folder path 30 | */ 31 | function createSettings() { 32 | var dialog = new Window ("dialog", "Settings"); 33 | 34 | // filename 35 | var fileNameGroup = dialog.add ("group"); 36 | fileNameGroup.orientation = "row"; 37 | fileNameGroup.alignment = "center"; 38 | var fileNameLabel = fileNameGroup.add("statictext", undefined, "File name"); 39 | var fileNameInput = fileNameGroup.add("edittext", [15,30,305,50], fileName); 40 | 41 | // file size 42 | var fileSizeGroup = dialog.add("panel",undefined,'Size of the mdpi (px)'); 43 | 44 | var fileSizeRelatedCheckbox = fileSizeGroup.add("checkbox", undefined, "Same width and height"); 45 | 46 | var fileSizeWidthGroup = fileSizeGroup.add ("group"); 47 | fileSizeWidthGroup.orientation = "row"; 48 | fileSizeWidthGroup.alignment = "center"; 49 | var fileSizeWidthLabel = fileSizeWidthGroup.add("statictext", undefined, "Width"); 50 | var fileSizeWidthInput = fileSizeWidthGroup.add("edittext", [15,30,60,50], mdpiWidth); 51 | 52 | var fileSizeHeightGroup = fileSizeGroup.add ("group"); 53 | fileSizeHeightGroup.orientation = "row"; 54 | fileSizeHeightGroup.alignment = "center"; 55 | var fileSizeHeightLabel = fileSizeHeightGroup.add("statictext", undefined, "Height"); 56 | var fileSizeHeightInput = fileSizeHeightGroup.add("edittext", [15,30,60,50], mdpiHeight); 57 | 58 | // file type 59 | var fileTypeGroup = dialog.add("panel",undefined,'File type'); 60 | fileTypeGroup.orientation="row"; 61 | var pngType=fileTypeGroup.add("radiobutton", undefined, "png"); 62 | pngType.value = isPng; 63 | var jpgType=fileTypeGroup.add("radiobutton", undefined, "jpg"); 64 | jpgType.value = !isPng; 65 | 66 | // custom folder path 67 | var customFolderPathBt = dialog.add ("button", undefined, "Use custom folder path"); 68 | 69 | // buttons 70 | var btnGroup = dialog.add ("group"); 71 | btnGroup.orientation = "row"; 72 | btnGroup.alignment = "center"; 73 | var okButton = btnGroup.add ("button", undefined, "OK"); 74 | var cancelButton = btnGroup.add ("button", undefined, "Cancel"); 75 | 76 | 77 | // user interaction functions 78 | fileNameInput.onChange = function() { 79 | fileName = fileNameInput.text; 80 | } 81 | 82 | fileSizeRelatedCheckbox.onClick = function () { 83 | if (fileSizeRelatedCheckbox.value === true) { 84 | fileSizeHeightInput.text = fileSizeWidthInput.text; 85 | mdpiHeight = mdpiWidth; 86 | } 87 | } 88 | 89 | fileSizeHeightInput.onChanging = function() { 90 | if (isNaN(fileSizeHeightInput.text)) { 91 | fileSizeHeightInput.text = mdpiHeight; 92 | } else { 93 | mdpiHeight = fileSizeHeightInput.text; 94 | // if the size are the same, also change the width 95 | if (fileSizeRelatedCheckbox.value === true) { 96 | // don't set again the height value, which will set the width, which will set the height... 97 | fileSizeRelatedCheckbox.value = false; 98 | fileSizeWidthInput.text = mdpiHeight; 99 | mdpiWidth = mdpiHeight; 100 | fileSizeRelatedCheckbox.value = true; 101 | } 102 | } 103 | } 104 | 105 | fileSizeWidthInput.onChanging = function() { 106 | if (isNaN(fileSizeWidthInput.text)) { 107 | fileSizeWidthInput.text = mdpiWidth; 108 | } else { 109 | mdpiWidth = fileSizeWidthInput.text; 110 | // if the size are the same, also change the width 111 | if (fileSizeRelatedCheckbox.value === true) { 112 | // don't set again the width value, which will set the height, which will set the width... 113 | fileSizeRelatedCheckbox.value = false; 114 | fileSizeHeightInput.text = mdpiWidth; 115 | mdpiHeight = mdpiWidth; 116 | fileSizeRelatedCheckbox.value = true; 117 | } 118 | } 119 | } 120 | 121 | pngType.onClick = function() { 122 | isPng = pngType.value; 123 | } 124 | 125 | jpgType.onClick = function() { 126 | isPng = !jpgType.value; 127 | } 128 | 129 | customFolderPathBt.onClick = function () { 130 | var folderPathTmp = Folder.selectDialog("Select folder where you want to put the drawable-mdpi folder and others"); 131 | if (folderPathTmp != null) { 132 | folderPath = folderPathTmp; 133 | } 134 | } 135 | 136 | okButton.onClick = function() { 137 | dialog.hide(); 138 | saveAllResources(); 139 | } 140 | 141 | dialog.center(); 142 | return dialog; 143 | } 144 | 145 | /** 146 | * Save the current document into the resource folders 147 | */ 148 | function saveAllResources() { 149 | // save current ruler unit settings, so we can restore it 150 | var ru = app.preferences.rulerUnits; 151 | 152 | // set ruler units to pixel to ensure scaling works as expected 153 | app.preferences.rulerUnits = Units.PIXELS; 154 | 155 | if (getSelectedLayersCount() == 0) { 156 | selectAllLayers(); 157 | } 158 | 159 | saveFile(folderPath+'/drawable-xxxhdpi', mdpiWidth * 4, mdpiHeight * 4); 160 | saveFile(folderPath+'/drawable-xxhdpi', mdpiWidth * 3, mdpiHeight * 3); 161 | saveFile(folderPath+'/drawable-xhdpi', mdpiWidth * 2, mdpiHeight * 2); 162 | saveFile(folderPath+'/drawable-hdpi', mdpiWidth * 1.5, mdpiHeight * 1.5); 163 | saveFile(folderPath+'/drawable-mdpi', mdpiWidth, mdpiHeight); 164 | 165 | // confirm to the user that operation went well and remind him the paths 166 | if (isPng) { 167 | extension = ".png"; 168 | } else { 169 | extension = ".jpg"; 170 | } 171 | alert("Your files have been save in " + folderPath + " with name " + fileName + extension); 172 | 173 | // restore old ruler unit settings 174 | app.preferences.rulerUnits = ru; 175 | } 176 | 177 | /** 178 | * Save the doc into folderPath with the given width and heigth 179 | */ 180 | function saveFile(folderPath, width, height) { 181 | dupToNewFile(); 182 | var docRef2 = app.activeDocument; 183 | resizeDoc(docRef2, width, height); 184 | 185 | fileName = fileName.replace(/\.[^\.]+$/, ''); 186 | var folder = Folder(folderPath); 187 | var extension, 188 | sfwOptions; 189 | if (isPng) { 190 | extension = ".png"; 191 | 192 | sfwOptions = new ExportOptionsSaveForWeb(); 193 | sfwOptions.format = SaveDocumentType.PNG; 194 | sfwOptions.includeProfile = false; 195 | sfwOptions.interlaced = 0; 196 | sfwOptions.optimized = true; 197 | sfwOptions.quality = 100; 198 | sfwOptions.PNG8 = false; 199 | } else { 200 | extension = ".jpg"; 201 | 202 | sfwOptions = new ExportOptionsSaveForWeb(); 203 | sfwOptions.format = SaveDocumentType.JPEG; 204 | sfwOptions.quality = 100; 205 | sfwOptions.includeProfile = true; 206 | sfwOptions.optimised = true; 207 | } 208 | 209 | if(!folder.exists) { 210 | folder.create(); 211 | } 212 | 213 | var saveFile = File(folder + "/" + fileName + extension); 214 | 215 | // Export the layer as a PNG 216 | activeDocument.exportDocument(saveFile, ExportType.SAVEFORWEB, sfwOptions); 217 | 218 | // Close the document without saving 219 | activeDocument.close(SaveOptions.DONOTSAVECHANGES); 220 | } 221 | 222 | /** 223 | * Test if the document is unsaved 224 | * http://2.adobe-photoshop-scripting.overzone.net/determine-if-file-has-never-been-saved-in-javascript-t264.html 225 | */ 226 | function isDocumentUnsaved(doc){ 227 | // assumes doc is the activeDocument 228 | cTID = function(s) { return app.charIDToTypeID(s); } 229 | var ref = new ActionReference(); 230 | ref.putEnumerated( cTID("Dcmn"), 231 | cTID("Ordn"), 232 | cTID("Trgt") ); //activeDoc 233 | var desc = executeActionGet(ref); 234 | var rc = true; 235 | if (desc.hasKey(cTID("FilR"))) { //FileReference 236 | var path = desc.getPath(cTID("FilR")); 237 | 238 | if (path) { 239 | rc = (path.absoluteURI.length == 0); 240 | } 241 | } 242 | return rc; 243 | }; 244 | 245 | /** 246 | * Resize the given document with width and height 247 | */ 248 | function resizeDoc(document, width, height) { 249 | 250 | document.resizeImage(UnitValue(width,"px"),UnitValue(height,"px"),null,ResampleMethod.BICUBIC); 251 | 252 | // Merge all layers inside the temp document 253 | activeLayer2.merge(); 254 | } 255 | 256 | /** 257 | * create a new document to work with 258 | */ 259 | function dupToNewFile() { 260 | var fileName = activeLayer.name.replace(/\.[^\.]+$/, ''), 261 | calcWidth = Math.ceil(activeLayer.bounds[2] - activeLayer.bounds[0]), 262 | calcHeight = Math.ceil(activeLayer.bounds[3] - activeLayer.bounds[1]), 263 | docResolution = docRef.resolution, 264 | document = app.documents.add(calcWidth, calcHeight, docResolution, fileName, NewDocumentMode.RGB, 265 | DocumentFill.TRANSPARENT); 266 | 267 | app.activeDocument = docRef; 268 | 269 | // Duplicated selection to a temp document 270 | activeLayer.duplicate(document, ElementPlacement.INSIDE); 271 | 272 | // Set focus on temp document 273 | app.activeDocument = document; 274 | 275 | // Assign a variable to the layer we pasted inside the temp document 276 | activeLayer2 = document.activeLayer; 277 | 278 | // Center the layer 279 | activeLayer2.translate(-activeLayer2.bounds[0],-activeLayer2.bounds[1]); 280 | } 281 | 282 | /** 283 | * Return the number of selected layers 284 | * source : http://www.ps-scripts.com/bb/viewtopic.php?t=4724 285 | */ 286 | function getSelectedLayersCount(){ 287 | var res = new Number(); 288 | var ref = new ActionReference(); 289 | ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 290 | var desc = executeActionGet(ref); 291 | if( desc.hasKey( stringIDToTypeID( 'targetLayers' ) ) ){ 292 | desc = desc.getList( stringIDToTypeID( 'targetLayers' )); 293 | res = desc.count 294 | }else{ 295 | var vis = app.activeDocument.activeLayer.visible; 296 | if(vis == true) app.activeDocument.activeLayer.visible = false; 297 | checkVisibility(); 298 | if(app.activeDocument.activeLayer.visible == true){ 299 | res =1; 300 | }else{ 301 | res = 0; 302 | } 303 | app.activeDocument.activeLayer.visible = vis; 304 | } 305 | return res; 306 | } 307 | 308 | function checkVisibility(){ 309 | var desc = new ActionDescriptor(); 310 | var list = new ActionList(); 311 | var ref = new ActionReference(); 312 | ref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt') ); 313 | list.putReference( ref ); 314 | desc.putList( charIDToTypeID('null'), list ); 315 | executeAction( charIDToTypeID('Shw '), desc, DialogModes.NO ); 316 | } 317 | 318 | /** 319 | * Select all layers of the document 320 | * source : Flatten All Layer Effects.jsx in the default scripts 321 | */ 322 | function selectAllLayers() { 323 | var idselectAllLayers = stringIDToTypeID( "selectAllLayers" ); 324 | var desc252 = new ActionDescriptor(); 325 | var idnull = charIDToTypeID( "null" ); 326 | var ref174 = new ActionReference(); 327 | var idLyr = charIDToTypeID( "Lyr " ); 328 | var idOrdn = charIDToTypeID( "Ordn" ); 329 | var idTrgt = charIDToTypeID( "Trgt" ); 330 | ref174.putEnumerated( idLyr, idOrdn, idTrgt ); 331 | desc252.putReference( idnull, ref174 ); 332 | executeAction( idselectAllLayers, desc252, DialogModes.NO ); 333 | } 334 | 335 | /** 336 | * Entry function 337 | * Init vars and display the settings dialog 338 | */ 339 | function main() { 340 | // save current ruler unit settings, so we can restore it 341 | var ru = app.preferences.rulerUnits; 342 | 343 | // set ruler units to pixel to ensure scaling works as expected 344 | app.preferences.rulerUnits = Units.PIXELS; 345 | 346 | if (app != null && app.activeDocument != null) 347 | { 348 | // init vars 349 | docRef = app.activeDocument; 350 | activeLayer = docRef.activeLayer; 351 | mdpiWidth = docRef.width.value; 352 | mdpiHeight = docRef.width.value; 353 | folderPath = docRef.path; 354 | fileName = docRef.name.replace(/\.[^\.]+$/, ''); 355 | isPng = true; 356 | 357 | // if the document is save, save the resource 358 | if(!isDocumentUnsaved()) { 359 | var settings = createSettings(); 360 | settings.show(); 361 | } else { 362 | alert ("Please save your document before using this script"); 363 | } 364 | } 365 | 366 | // restore old ruler unit settings 367 | app.preferences.rulerUnits = ru; 368 | } 369 | 370 | /****************************************/ 371 | /* Functions call */ 372 | /****************************************/ 373 | 374 | // Run main function 375 | main(); --------------------------------------------------------------------------------