Documents en lien avec mon rapport de stage. Stage de développement d'une application Android au sein du bureau d'étude de l'entreprise AllianTech.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Export QML.jsx 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. /*
  2. Photoshop to QML Exporter
  3. Version: 0.4
  4. For information about Qt Quick itself:
  5. http://qt-project.org/doc/qt-5.0/qtquick/qtquick-index.html
  6. Author: Jens Bache-wiig
  7. contact: jens.bache-wiig@digia.com
  8. Copyright (c) 2013 Digia Plc and/or its subsidiary(-ies).
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions are met:
  11. 1. Redistributions of source code must retain the above copyright
  12. notice, this list of conditions and the following disclaimer.
  13. 2. Redistributions in binary form must reproduce the above copyright
  14. notice, this list of conditions and the following disclaimer in the
  15. documentation and/or other materials provided with the distribution.
  16. 3. All advertising materials mentioning features or use of this software
  17. must display the following acknowledgement:
  18. This product includes software developed by the <organization>.
  19. 4. Neither the name of the <organization> nor the
  20. names of its contributors may be used to endorse or promote products
  21. derived from this software without specific prior written permission.
  22. THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
  23. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  24. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  25. DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  26. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  27. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  28. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  29. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. */
  33. #target photoshop
  34. var mainDialog
  35. var progressPanel
  36. var runButton = 1
  37. var cancelButton = 2
  38. var qmlfile
  39. var layerCount = 0
  40. var layerIndex = 0
  41. var cancelExport = 0
  42. // Setting keys
  43. var appString = "QML Exporter - 1"
  44. var outputNameKey = 0;
  45. var destinationKey = 1;
  46. var rasterizeKey = 2;
  47. var exportByGroupKey = 3;
  48. var exportHidden = 4;
  49. var exportQML = 5;
  50. Array.prototype.indexOf = function(elt /*, from*/)
  51. {
  52. var len = this.length;
  53. var from = Number(arguments[1]) || 0;
  54. from = (from < 0)
  55. ? Math.ceil(from)
  56. : Math.floor(from);
  57. if (from < 0)
  58. from += len;
  59. for (; from < len; from++)
  60. {
  61. if (from in this &&
  62. this[from] === elt)
  63. return from;
  64. }
  65. return -1;
  66. };
  67. main();
  68. function hexValue(dec)
  69. {
  70. var result;
  71. switch (dec) {
  72. case 10:
  73. result = "a";
  74. break;
  75. case 11:
  76. result = "b"
  77. break;
  78. case 12:
  79. result = "c";
  80. break;
  81. case 13:
  82. result = "d";
  83. break;
  84. case 14:
  85. result = "e"
  86. case 15:
  87. result = "f"
  88. break;
  89. default:
  90. result = dec
  91. break;
  92. }
  93. return result;
  94. }
  95. // Converts SolidColor to a QML color property
  96. function qtColor(color) {
  97. var r = Math.floor(color.rgb.red)
  98. var g = Math.floor(color.rgb.green);
  99. var b = Math.floor(color.rgb.blue)
  100. var a = Math.floor(color.rgb.alpha * 255)
  101. var v1 = hexValue(Math.floor(r / 16));
  102. var v2 = hexValue(r % 16);
  103. var v3 = hexValue(Math.floor(g / 16));
  104. var v4 = hexValue(g % 16);
  105. var v5 = hexValue(Math.floor(b / 16));
  106. var v6 = hexValue(b % 16);
  107. if (a > 0) {
  108. var v7 = hexValue(Math.floor(a / 16));
  109. var v8 = hexValue(a % 16);
  110. return "\"#" + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + "\"";
  111. }
  112. return "\"#" + v1 + v2 + v3 + v4 + v5 + v6 + "\"";
  113. }
  114. function main() {
  115. var exportInfo = new Object();
  116. if (cancelButton == setupDialog(exportInfo))
  117. return 'cancel';
  118. var myMaximumValue = 1.0;
  119. var myProgressBarWidth = 300;
  120. progressPanel = new Window('window', 'Exporting document to QML...');
  121. progressPanel.myProgressBar = progressPanel.add('progressbar', [12, 12, myProgressBarWidth, 24], 0, myMaximumValue);
  122. progressPanel.buttonCancel = progressPanel .add("button", undefined, "Cancel");
  123. progressPanel.buttonCancel.onClick = function () {
  124. cancelExport = true;
  125. progressPanel.hide();
  126. }
  127. progressPanel.show();
  128. app.preferences.rulerUnits = Units.PIXELS
  129. var documentName = app.activeDocument.name;
  130. app.activeDocument = app.documents[documentName];
  131. var documentCopy = app.activeDocument.duplicate();
  132. documentCopy.activeLayer = documentCopy.layers[documentCopy.layers.length - 1];
  133. var elementName = mainDialog.outputName.text;
  134. if (elementName.indexOf(".qml") == -1) // Append .qml unless not explicitly set
  135. elementName += ".qml"
  136. var outputName = exportInfo.destination + "/" + elementName;
  137. var imagefolder = new Folder(exportInfo.destination + "/images/");
  138. imagefolder.create();
  139. app.activeDocument.suspendHistory("export QML history", "");
  140. var exportQML = mainDialog.exportQML.value
  141. if (exportQML && !cancelExport) {
  142. qmlfile = new File(outputName);
  143. qmlfile.encoding = "UTF8";
  144. qmlfile.open("w", "TEXT", "");
  145. qmlfile.write("import Qt 4.7\n");
  146. qmlfile.write("Item {\n");
  147. qmlfile.write(" width:" + app.activeDocument.width.as("px") + "\n");
  148. qmlfile.write(" height:" + app.activeDocument.height.as("px") + "\n");
  149. }
  150. countLayers(documentCopy) // For progressBar
  151. exportChildren(documentCopy, app.documents[documentName], exportInfo, documentCopy, exportInfo.fileNamePrefix);
  152. if (exportQML) {
  153. documentCopy.close(SaveOptions.DONOTSAVECHANGES);
  154. qmlfile.write("}\n");
  155. qmlfile.close();
  156. }
  157. Panel.hide();
  158. }
  159. function setupDialog(exportInfo) {
  160. mainDialog = new Window("dialog", "Export Document To QML");
  161. var brush = mainDialog.graphics.newBrush(mainDialog.graphics.BrushType.THEME_COLOR, "appDialogBackground");
  162. mainDialog.graphics.backgroundColor = brush;
  163. mainDialog.graphics.disabledBackgroundColor = mainDialog.graphics.backgroundColor;
  164. mainDialog.orientation = 'column';
  165. mainDialog.alignChildren = 'left';
  166. mainDialog.groupFirstLine = mainDialog.add("group");
  167. mainDialog.groupFirstLine.orientation = 'row';
  168. mainDialog.groupFirstLine.alignChildren = 'left';
  169. mainDialog.groupFirstLine.alignment = 'fill';
  170. mainDialog.groupSecondLine = mainDialog.add("group");
  171. mainDialog.groupSecondLine.orientation = 'row';
  172. mainDialog.groupSecondLine.alignChildren = 'left';
  173. mainDialog.groupThirdLine = mainDialog.add("group");
  174. mainDialog.groupThirdLine.orientation = 'row';
  175. mainDialog.groupThirdLine.alignChildren = 'right';
  176. mainDialog.groupThirdLine.alignment = 'right';
  177. mainDialog.groupFourthLine = mainDialog.add("group");
  178. mainDialog.groupFourthLine.orientation = 'row';
  179. mainDialog.groupFourthLine.alignChildren = 'right';
  180. mainDialog.groupFourthLine.alignment = 'right';
  181. mainDialog.groupFirstLine.add("statictext", undefined, "Element Name:");
  182. mainDialog.groupSecondLine.add("statictext", undefined, "Output Folder:");
  183. mainDialog.outputName = mainDialog.groupFirstLine.add("edittext", undefined, "MyElement");
  184. mainDialog.outputName.preferredSize.width = 220
  185. mainDialog.rasterizeText = mainDialog.groupThirdLine.add("checkbox", undefined, "Rasterize Text");
  186. mainDialog.exportByGroup = mainDialog.groupThirdLine.add("checkbox", undefined, "Group layers");
  187. mainDialog.exportHidden = mainDialog.groupThirdLine.add("checkbox", undefined, "Export hidden");
  188. mainDialog.destinationFolder = mainDialog.groupSecondLine.add("edittext", undefined, "");
  189. mainDialog.destinationFolder.preferredSize.width = 220;
  190. mainDialog.buttonBrowse = mainDialog.groupSecondLine.add("button", undefined, "Browse..");
  191. mainDialog.exportQML = mainDialog.groupThirdLine.add("checkbox", undefined, "Export QML");
  192. mainDialog.buttonRun = mainDialog.groupFourthLine .add("button", undefined, "Export");
  193. mainDialog.defaultElement = mainDialog.buttonRun
  194. mainDialog.buttonBrowse.onClick = function () {
  195. var defaultFolder = defaultFolder = "~";
  196. var selFolder = Folder.selectDialog("Select destination", defaultFolder);
  197. if (selFolder != null)
  198. mainDialog.destinationFolder.text = selFolder.fsName;
  199. }
  200. mainDialog.buttonRun.onClick = function () {
  201. var destination = mainDialog.destinationFolder.text;
  202. if (destination.length == 0) {
  203. alert("you must specify a destination directory.");
  204. return;
  205. }
  206. var testFolder = new Folder(destination);
  207. if (!testFolder.exists) {
  208. alert("The destination directory does not exist.");
  209. return;
  210. }
  211. exportInfo.destination = destination;
  212. mainDialog.close(runButton);
  213. }
  214. mainDialog.buttonCancel = mainDialog.groupFourthLine .add("button", undefined, "Cancel");
  215. mainDialog.buttonCancel.onClick = function () {
  216. mainDialog.close(cancelButton);
  217. }
  218. try {
  219. // Try to read saved settings
  220. var desc = app.getCustomOptions(appString);
  221. mainDialog.outputName.text = desc.getString(outputNameKey);
  222. mainDialog.destinationFolder.text = desc.getString(destinationKey);
  223. mainDialog.rasterizeText.value = desc.getBoolean(rasterizeKey);
  224. mainDialog.exportByGroup.value = desc.getBoolean(exportByGroupKey);
  225. mainDialog.exportHidden.value = desc.getBoolean(exportHidden);
  226. mainDialog.exportQML.value = desc.getBoolean(exportQML);
  227. }
  228. catch(e) {
  229. // Default settings on first run
  230. mainDialog.exportByGroup.value = true;
  231. mainDialog.exportHidden.value = false;
  232. mainDialog.exportQML.value = true;
  233. } // Use defaults
  234. app.bringToFront();
  235. mainDialog.defaultElement.active = true;
  236. mainDialog.center();
  237. var result = mainDialog.show();
  238. if (cancelButton != result) {
  239. var desc = new ActionDescriptor();
  240. desc.putString(outputNameKey, mainDialog.outputName.text);
  241. desc.putString(destinationKey, mainDialog.destinationFolder.text);
  242. desc.putBoolean(rasterizeKey, mainDialog.rasterizeText.value);
  243. desc.putBoolean(exportByGroupKey, mainDialog.exportByGroup.value);
  244. desc.putBoolean(exportHidden, mainDialog.exportHidden.value);
  245. desc.putBoolean(exportQML, mainDialog.exportQML.value);
  246. app.putCustomOptions(appString, desc);
  247. }
  248. return result;
  249. }
  250. function countLayers(obj) {
  251. // Even when grouping layers, we export all layers at depth == 0
  252. for (var i = 0; i < obj.artLayers.length; i++) {
  253. layerCount++;
  254. }
  255. for (var i = 0; i < obj.layerSets.length; i++) { // Recursive
  256. if (!mainDialog.exportByGroup.value)
  257. countLayers(obj.layerSets[i]);
  258. layerCount++;
  259. }
  260. }
  261. function hideAll(obj) {
  262. for (var i = 0; i < obj.artLayers.length; i++) {
  263. obj.artLayers[i].allLocked = false;
  264. obj.artLayers[i].visible = false;
  265. }
  266. for (var i = 0; i < obj.layerSets.length; i++) { // Recursive
  267. hideAll(obj.layerSets[i]);
  268. }
  269. }
  270. function exportChildren(dupObj, orgObj, exportInfo, dupDocRef, fileNamePrefix) {
  271. var exportQML = mainDialog.exportQML.value
  272. // Track names to detect duplicates
  273. var names = new Array;
  274. var uniqueCounter = 1;
  275. if (!mainDialog.exportByGroup.value)
  276. hideAll(dupObj)
  277. for (var i = dupObj.layers.length - 1; i >= 0 && !cancelExport ; i--) {
  278. progressPanel.myProgressBar.value = (layerIndex++)/layerCount
  279. var currentLayer = dupObj.layers[i];
  280. // Ensure unique layer names
  281. while (names[currentLayer.name]) {
  282. $.writeln("Warning: duplicate layer name: " + currentLayer.name);
  283. currentLayer.name = orgObj.layers[i].name+ "_#" + uniqueCounter++;
  284. }
  285. names[currentLayer.name] = true;
  286. // Skip hidden layers
  287. var visible = true;
  288. if (!orgObj.layers[i].visible) {
  289. visible = false
  290. }
  291. if (!mainDialog.exportByGroup.value) {
  292. // Ignore layer groups and only show one layer at once
  293. if (currentLayer.typename== "LayerSet")
  294. continue;
  295. dupObj.layers[i].visible = true
  296. } else {
  297. // Hide all but current layergroup
  298. for (var k = dupObj.layers.length - 1; k >= 0; k--)
  299. dupObj.layers[k].visible = (k==i)
  300. }
  301. if (!visible && !mainDialog.exportHidden.value)
  302. continue;
  303. // Since we already save opacity, we dont want it affecting the output image
  304. var opacity = currentLayer.opacity / 100.0;
  305. currentLayer.opacity = 100;
  306. var layerName = dupObj.layers[i].name; // store layer name before change doc
  307. var fileNameBody = layerName.toLowerCase();
  308. // Ignore empty text layers
  309. if (currentLayer.kind == LayerKind.TEXT && currentLayer.textItem.contents == "") continue;
  310. var documentCopyTmp = dupDocRef.duplicate();
  311. // Trim copied document to layer bounds
  312. if (activeDocument.activeLayer.isBackgroundLayer == false) {
  313. // app.activeDocument.trim(TrimType.TRANSPARENT);
  314. var bounds = currentLayer.bounds
  315. activeDocument.crop (bounds, 0, bounds.width, bounds.height)
  316. }
  317. fileNameBody = fileNameBody.replace(/[ :\/\\*\?\"\<\>\|#]/g, "_"); // '/\:*?"<>|' -> '_'
  318. if (fileNameBody.length > 120) {
  319. fileNameBody = fileNameBody.substring(0, 120);
  320. }
  321. var isText = (currentLayer.kind == LayerKind.TEXT && !(mainDialog.rasterizeText.value))
  322. var filename = fileNameBody + ".png";
  323. if (exportQML) {
  324. // Write QML properties
  325. if (isText) qmlfile.write(" Text {\n");
  326. else qmlfile.write(" Image {\n");
  327. qmlfile.write(" id: " + fileNameBody + "\n");
  328. if (!visible)
  329. qmlfile.write(" visible: " + visible+ "\n");
  330. var xoffset = currentLayer.bounds[0].as("px");
  331. var yoffset = currentLayer.bounds[1].as("px");
  332. if (isText) {
  333. var textItem = currentLayer.textItem;
  334. qmlfile.write(" text: \"" + textItem.contents + "\"\n");
  335. // ### Temporary hack to set font positioning
  336. // Using pointsize doesnt work for some reason and we need to
  337. // figure out which metric we need to use ascending, perhaps?
  338. yoffset -= textItem.size.as("px") / 4;
  339. qmlfile.write(" font.pixelSize: " + Math.floor(textItem.size.as("px")) + "\n");
  340. //var fontfamily = app.textFonts.getByName(textitem.font);
  341. qmlfile.write(" font.family: \"" + textItem.font + "\"\n");
  342. if (textItem.font.indexOf("Bold") != -1) qmlfile.write(" font.bold: true\n");
  343. if (textItem.font.indexOf("Italic") != -1) qmlfile.write(" font.italic: true\n");
  344. qmlfile.write(" color: " + qtColor(currentLayer.textItem.color) + "\n");
  345. qmlfile.write(" smooth: true\n");
  346. } else {
  347. qmlfile.write(" source: \"images/" + filename + "\"\n");
  348. }
  349. qmlfile.write(" x: " + xoffset + "\n");
  350. qmlfile.write(" y: " + yoffset + "\n");
  351. qmlfile.write(" opacity: " + opacity + "\n");
  352. qmlfile.write(" }\n");
  353. }
  354. // Save document
  355. if (!isText) {
  356. var saveFile = new File(exportInfo.destination + "/images/" + filename);
  357. pngSaveOptions = new PNGSaveOptions();
  358. pngSaveOptions.interlaced = false;
  359. documentCopyTmp.saveAs(saveFile, pngSaveOptions, true, Extension.LOWERCASE);
  360. }
  361. // Close tempfile
  362. documentCopyTmp.close(SaveOptions.DONOTSAVECHANGES);
  363. if (!mainDialog.exportByGroup.value)
  364. dupObj.layers[i].visible = false
  365. }
  366. for (var i = 0; i < dupObj.layerSets.length; i++) {
  367. var fileNameBody = fileNamePrefix;
  368. fileNameBody += "_" + "s";
  369. if (!mainDialog.exportByGroup.value)
  370. exportChildren(dupObj.layerSets[i], orgObj.layerSets[i], exportInfo, dupDocRef, fileNameBody); // recursive call
  371. }
  372. }
  373. function collectLayerSets (theParent) {
  374. if (!allLayerSets) {
  375. var allLayerSets = new Array
  376. }
  377. for (var m = theParent.layers.length - 1; m >= 0;m--) {
  378. var theLayer = theParent.layers[m];
  379. // No need to process hidden layers
  380. if (theLayer.typename == "LayerSet") {
  381. allLayerSets = allLayerSets.concat(theLayer);
  382. allLayerSets = allLayerSets.concat(collectLayerSets(theLayer))
  383. }
  384. }
  385. return allLayerSets
  386. };