├── LICENSE ├── README.md └── sketch-export-sizes-generator.sketchplugin └── Contents └── Sketch ├── manifest.json └── script.cocoascript /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Suyeol Jeon (xoul.kr) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sketch Export Sizes Generator 2 | ============================= 3 | 4 | [![release](http://img.shields.io/github/release/devxoul/sketch-export-sizes-generator.svg)](https://github.com/devxoul/sketch-export-sizes-generator/releases) 5 | 6 | A simple plugin that generates export sizes of layers. 7 | 8 | 9 | Installation 10 | ------------ 11 | 12 | 1. Download the latest plugin at [here](https://github.com/devxoul/sketch-export-sizes-generator/releases/latest). 13 | 14 | 2. Unzip it and open the `sketch-export-sizes-generator.sketchplugin` file. 15 | 16 | ![install](https://cloud.githubusercontent.com/assets/931655/9568335/83e069e8-4f81-11e5-9ce9-519b58a9023d.png) 17 | 18 | 19 | Usage 20 | ----- 21 | 22 | 1. Select layers. 23 | 24 | 2. Select [Plugins] - [Export Sizes Generator] - [Generate Sizes] from the menu. 25 | > Shortcut: Command + Option + Shift + E 26 | 27 | ![menu](https://cloud.githubusercontent.com/assets/931655/9568280/ad573d6c-4f7f-11e5-903a-c9bb10c31040.png) 28 | 29 | 3. Enter sizes and click OK. 30 | > Default: `29, 40, 60` for size and `2, 3` for scale. (iOS App) 31 | 32 | ![prompt](https://cloud.githubusercontent.com/assets/931655/11877130/b6a3057a-a530-11e5-9385-cf8585a53b9b.png) 33 | 34 | 4. Export sizes are created. 35 | 36 | ![export](https://cloud.githubusercontent.com/assets/931655/11877222/3bf9731c-a531-11e5-8f92-367c92b7122d.png) 37 | 38 | 39 | 40 | 5. Congratulations! You can now export icons with various sizes. 41 | 42 | 43 | License 44 | ------- 45 | 46 | **Sketch Export Sizes Generator** is under MIT license. See the LICENSE file for more info. 47 | -------------------------------------------------------------------------------- /sketch-export-sizes-generator.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author" : "Suyeol Jeon", 3 | "commands" : [ 4 | { 5 | "script" : "script.cocoascript", 6 | "handler" : "onRun", 7 | "shortcut" : "shift alt cmd e", 8 | "name" : "Generate Sizes", 9 | "identifier" : "Generate Sizes" 10 | } 11 | ], 12 | "menu" : { 13 | "items" : [ 14 | "Generate Sizes" 15 | ], 16 | "title" : "Export Sizes Generator" 17 | }, 18 | "identifier" : "kr.xoul.sketch.export-sizes-generator", 19 | "version" : "1.2.1", 20 | "description" : "A simple plugin that generates export sizes of layers.", 21 | "authorEmail" : "devxoul@gmail.com", 22 | "name" : "Export Sizes Generator" 23 | } 24 | -------------------------------------------------------------------------------- /sketch-export-sizes-generator.sketchplugin/Contents/Sketch/script.cocoascript: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Suyeol Jeon (xoul.kr) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, 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, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | 24 | var RecentGeneratedExportableSize = 'RecentGeneratedExportableSize' 25 | var RecentGeneratedExportableScale = 'RecentGeneratedExportableScale' 26 | 27 | var SIZES = []; // e.g. [16, 32, 128, 256, 512] 28 | var SCALES = []; // e.g. [1, 2] 29 | 30 | var LABEL_WIDTH = 50; 31 | var INPUT_WIDTH = 200; 32 | 33 | function createLabelAtIndex(index, text) { 34 | var y = 30 * index + 5; 35 | var label = [[NSTextField alloc] initWithFrame:NSMakeRect(0, y, LABEL_WIDTH, 0)]; 36 | label.backgroundColor = NSColor.clearColor(); 37 | label.bordered = false; 38 | label.selectable = false; 39 | label.stringValue = text; 40 | label.sizeToFit(); 41 | return label; 42 | } 43 | 44 | 45 | function createInputAtIndex(index) { 46 | var y = 30 * index; 47 | var textField = [[NSTextField alloc] initWithFrame:NSMakeRect(LABEL_WIDTH, y, INPUT_WIDTH, 24)]; 48 | textField.cell().setWraps(false); 49 | textField.cell().setScrollable(true); 50 | textField.cell().setUsesSingleLineMode(true); 51 | return textField; 52 | } 53 | 54 | 55 | function numberValuesFromString(string) { 56 | return string.split(/,\s*/).map(function (str) { 57 | return parseFloat(str); 58 | }); 59 | } 60 | 61 | 62 | function prompt() { 63 | var informativeText = [ 64 | 'iOS App Icon', 65 | ' - Sizes: 29, 40, 60', 66 | ' - Scales: 2, 3', 67 | '', 68 | 'Mac App Icon', 69 | ' - Sizes: 16, 32, 128, 256, 512', 70 | ' - Scales: 1, 2', 71 | ].join('\n'); 72 | var alert = [NSAlert alertWithMessageText:'Enter exportable size and scale.' 73 | defaultButton:'OK' 74 | alternateButton:'Cancel' 75 | otherButton:nil 76 | informativeTextWithFormat:informativeText]; 77 | 78 | var userDefaults = NSUserDefaults.standardUserDefaults(); 79 | 80 | var sizeLabel = createLabelAtIndex(1, "Size:"); 81 | var sizeInput = createInputAtIndex(1); 82 | sizeInput.stringValue = userDefaults.stringForKey(RecentGeneratedExportableSize) || '29, 40, 60'; 83 | 84 | var scaleLabel = createLabelAtIndex(0, "Scale:"); 85 | var scaleInput = createInputAtIndex(0); 86 | scaleInput.stringValue = userDefaults.stringForKey(RecentGeneratedExportableScale) || '2, 3'; 87 | 88 | var accessoryView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, LABEL_WIDTH + INPUT_WIDTH, 60)]; 89 | accessoryView.addSubview(sizeLabel); 90 | accessoryView.addSubview(sizeInput); 91 | accessoryView.addSubview(scaleLabel); 92 | accessoryView.addSubview(scaleInput); 93 | alert.setAccessoryView(accessoryView); 94 | 95 | var button = alert.runModal(); 96 | if (button == NSAlertDefaultReturn) { 97 | SIZES = numberValuesFromString(sizeInput.stringValue()); 98 | SCALES = numberValuesFromString(scaleInput.stringValue()); 99 | 100 | [userDefaults setValue:sizeInput.stringValue() 101 | forKey:RecentGeneratedExportableSize]; 102 | [userDefaults setValue:scaleInput.stringValue() 103 | forKey:RecentGeneratedExportableScale]; 104 | [userDefaults synchronize]; 105 | } 106 | } 107 | 108 | 109 | function compare(a, b) { 110 | return b - a; 111 | } 112 | 113 | 114 | function prepareExportSizes(layer) { 115 | log('=== ' + layer.name() + ' ==='); 116 | log('Remove all export sizes of layer \'' + layer.name() + '\'.'); 117 | var sizes = layer.exportOptions().exportFormats(); 118 | layer.exportOptions().removeAllExportFormats(); 119 | 120 | SIZES.sort(compare); 121 | SCALES.sort(compare); 122 | 123 | for (size of SIZES) { 124 | for (scale of SCALES) { 125 | var height = Math.floor(size * scale); 126 | var suffix = SIZES.length > 1 ? String(size) : ""; 127 | if (scale > 1) { 128 | suffix += '@' + scale + 'x'; 129 | } 130 | addExportSize(layer, height, suffix); 131 | } 132 | } 133 | } 134 | 135 | 136 | function addExportSize(layer, height, suffix, format) { 137 | if (!format) { 138 | format = 'png'; 139 | } 140 | 141 | log('Add \'' + layer.name() + suffix + '\' (' + height + 'h' + ')'); 142 | 143 | var size = layer.exportOptions().addExportFormat(); 144 | size.setFileFormat(format); 145 | size.setName(suffix); 146 | size.setScale(height / layer.frame().size().height); 147 | size.setVisibleScaleType(2); // e.g. 512h 148 | } 149 | 150 | var onRun = function(context) { 151 | var selectedLayers = context.selection; 152 | var selectedCount = selectedLayers.count(); 153 | var doc = context.document; 154 | 155 | if (selectedCount == 0) { 156 | log('No layers are selected.'); 157 | var alert = [NSAlert alertWithMessageText:'Oops!' 158 | defaultButton:'Cancel' 159 | alternateButton:nil 160 | otherButton:nil 161 | informativeTextWithFormat:'Please select a layer.']; 162 | alert.runModal(); 163 | return; 164 | } 165 | 166 | prompt(); 167 | 168 | if (SIZES.length == 0) { 169 | log('No valid sizes.'); 170 | return; 171 | } 172 | 173 | for (var i = 0; i < selectedCount; i++) { 174 | var layer = selectedLayers[i]; 175 | 176 | if (i != 0) { 177 | log(''); 178 | } 179 | prepareExportSizes(layer); 180 | 181 | // deselect and reselect all layers to force refresh UI 182 | doc.currentPage().deselectAllLayers(); 183 | [layer select:true byExpandingSelection:true]; 184 | } 185 | 186 | log('\nDone.'); 187 | }; 188 | --------------------------------------------------------------------------------