From 9359df776b77abac95494034eb094ad76dd826eb Mon Sep 17 00:00:00 2001 From: Jens Bache-Wiig Date: Mon, 18 Oct 2010 17:52:35 +0200 Subject: First version --- Export QML.jsx | 285 +++++++++++++++++++++++++++++ README.txt | 16 ++ example.psd | Bin 0 -> 588726 bytes example_output/MyElement.qml | 118 ++++++++++++ example_output/images/background.png | Bin 0 -> 55513 bytes example_output/images/background_text.png | Bin 0 -> 2990 bytes example_output/images/drop_shadow.png | Bin 0 -> 1773 bytes example_output/images/drop_shadow_1.png | Bin 0 -> 3241 bytes example_output/images/drop_shadow_2.png | Bin 0 -> 5981 bytes example_output/images/drop_shadow_3.png | Bin 0 -> 1996 bytes example_output/images/mybutton.png | Bin 0 -> 1125 bytes example_output/images/new_layer.png | Bin 0 -> 6890 bytes example_output/images/new_layer_1.png | Bin 0 -> 311 bytes example_output/images/new_layer_copy.png | Bin 0 -> 4842 bytes example_output/images/new_layer_copy_1.png | Bin 0 -> 4868 bytes example_output/images/new_layer_copy_2.png | Bin 0 -> 7899 bytes example_output/images/qml.png | Bin 0 -> 858 bytes 17 files changed, 419 insertions(+) create mode 100644 Export QML.jsx create mode 100644 README.txt create mode 100644 example.psd create mode 100644 example_output/MyElement.qml create mode 100644 example_output/images/background.png create mode 100644 example_output/images/background_text.png create mode 100644 example_output/images/drop_shadow.png create mode 100644 example_output/images/drop_shadow_1.png create mode 100644 example_output/images/drop_shadow_2.png create mode 100644 example_output/images/drop_shadow_3.png create mode 100644 example_output/images/mybutton.png create mode 100644 example_output/images/new_layer.png create mode 100644 example_output/images/new_layer_1.png create mode 100644 example_output/images/new_layer_copy.png create mode 100644 example_output/images/new_layer_copy_1.png create mode 100644 example_output/images/new_layer_copy_2.png create mode 100644 example_output/images/qml.png diff --git a/Export QML.jsx b/Export QML.jsx new file mode 100644 index 0000000..0b2403e --- /dev/null +++ b/Export QML.jsx @@ -0,0 +1,285 @@ +/* +Photoshop to QML Exporter + +Version: 0.1 + +For information about Qt Quick itself: +http://qt.nokia.com/products/qt-quick/ + +Author: Jens Bache-wiig +contact: jensbw@gmail.com + +Copyright (c) 2010, Nokia +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by the . +4. Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#target photoshop + +var mainDialog; +var runButton = 1; +var cancelButton = 2; +var qmlfile; + + +// Setting keys +var appString = "QML Exporter - 1" +var outputNameKey = 0; +var destinationKey = 1; +var rasterizeKey = 2; + +main(); + +// Converts SolidColor to a QML color property +function qtColor(color) { + return "Qt.rgba(" + color.rgb.red + "," + color.rgb.green + "," + color.rgb.blue + ", 1.0)"; +} + +function main() { + + var exportInfo = new Object(); + + if (cancelButton == setupDialog(exportInfo)) + return 'cancel'; + + app.preferences.rulerUnits = Units.PIXELS + + var documentName = app.activeDocument.name; + app.activeDocument = app.documents[documentName]; + var documentCopy = app.activeDocument.duplicate(); + documentCopy.activeLayer = documentCopy.layers[documentCopy.layers.length - 1]; + + + var elementName = mainDialog.outputName.text; + if (elementName.indexOf(".qml") == -1) // Append .qml unless not explicitly set + elementName += ".qml" + + var outputName = exportInfo.destination + "\\" + elementName; + + var imagefolder = new Folder(exportInfo.destination + "\\images\\"); + imagefolder.create(); + + app.activeDocument.suspendHistory("export QML history", ""); + qmlfile = new File(outputName); + qmlfile.encoding = "UTF8"; + qmlfile.open("w", "TEXT", ""); + qmlfile.write("import Qt 4.7\n"); + qmlfile.write("Rectangle {\n"); + qmlfile.write(" width:" + app.activeDocument.width.as("px") + "\n"); + qmlfile.write(" height:" + app.activeDocument.height.as("px") + "\n"); + exportChildren(documentCopy, app.documents[documentName], exportInfo, documentCopy, exportInfo.fileNamePrefix); + documentCopy.close(SaveOptions.DONOTSAVECHANGES); + qmlfile.write("}\n"); + qmlfile.close(); +} + + +function setupDialog(exportInfo) { + mainDialog = new Window("dialog", "Export Document To QML"); + var brush = mainDialog.graphics.newBrush(mainDialog.graphics.BrushType.THEME_COLOR, "appDialogBackground"); + mainDialog.graphics.backgroundColor = brush; + mainDialog.graphics.disabledBackgroundColor = mainDialog.graphics.backgroundColor; + mainDialog.orientation = 'column'; + mainDialog.alignChildren = 'left'; + + mainDialog.groupFirstLine = mainDialog.add("group"); + mainDialog.groupFirstLine.orientation = 'row'; + mainDialog.groupFirstLine.alignChildren = 'left'; + mainDialog.groupFirstLine.alignment = 'fill'; + + mainDialog.groupSecondLine = mainDialog.add("group"); + mainDialog.groupSecondLine.orientation = 'row'; + mainDialog.groupSecondLine.alignChildren = 'left'; + + mainDialog.groupThirdLine = mainDialog.add("group"); + mainDialog.groupThirdLine.orientation = 'row'; + mainDialog.groupThirdLine.alignChildren = 'right'; + mainDialog.groupThirdLine.alignment = 'right'; + + mainDialog.groupFirstLine.add("statictext", undefined, "Element Name:"); + mainDialog.groupSecondLine.add("statictext", undefined, "Output Folder:"); + + mainDialog.outputName = mainDialog.groupFirstLine.add("edittext", undefined, "MyElement"); + mainDialog.outputName.preferredSize.width = 120 + mainDialog.rasterizeText = mainDialog.groupFirstLine.add("checkbox", undefined, "Rasterize Text"); + + mainDialog.destinationFolder = mainDialog.groupSecondLine.add("edittext", undefined, ""); + mainDialog.destinationFolder.preferredSize.width = 200; + mainDialog.buttonBrowse = mainDialog.groupSecondLine.add("button", undefined, "Browse.."); + + mainDialog.buttonBrowse.onClick = function () { + var defaultFolder = defaultFolder = "~"; + var selFolder = Folder.selectDialog("Select destination", defaultFolder); + if (selFolder != null) mainDialog.destinationFolder.text = selFolder.fsName; + mainDialog.defaultElement.active = true; + } + + mainDialog.buttonRun = mainDialog.groupThirdLine.add("button", undefined, "Export"); + mainDialog.buttonRun.onClick = function () { + var destination = mainDialog.destinationFolder.text; + if (destination.length == 0) { + alert("you must specify a destination directory."); + return; + } + + var testFolder = new Folder(destination); + if (!testFolder.exists) { + alert("The destination directory does not exist."); + return; + } + exportInfo.destination = destination; + + mainDialog.close(runButton); + } + + mainDialog.buttonCancel = mainDialog.groupThirdLine.add("button", undefined, "Cancel"); + mainDialog.buttonCancel.onClick = function () { + mainDialog.close(cancelButton); + } + + try { + // Try to read saved settings + var desc = app.getCustomOptions(appString); + mainDialog.outputName.text = desc.getString(outputNameKey); + mainDialog.destinationFolder.text = desc.getString(destinationKey); + mainDialog.rasterizeText.value = desc.getBoolean(rasterizeKey); + } + catch(e) { } // Use defaults + + app.bringToFront(); + mainDialog.center(); + + var result = mainDialog.show(); + if (cancelButton != result) { + var desc = new ActionDescriptor(); + desc.putString(outputNameKey, mainDialog.outputName.text); + desc.putString(destinationKey, mainDialog.destinationFolder.text); + desc.putBoolean(rasterizeKey, mainDialog.rasterizeText.value); + app.putCustomOptions(appString, desc); + } + + return result; +} + +function hideAll(obj) { + for (var i = 0; i < obj.artLayers.length; i++) { + obj.artLayers[i].allLocked = false; + obj.artLayers[i].visible = false; + } + for (var i = 0; i < obj.layerSets.length; i++) { // Recursive + hideAll(obj.layerSets[i]); + } +} + +function exportChildren(dupObj, orgObj, exportInfo, dupDocRef, fileNamePrefix) { + hideAll(dupObj) + for (var i = dupObj.artLayers.length - 1; i >= 0; i--) { + if (i < dupObj.artLayers.length - 1) dupObj.artLayers[i + 1].visible = false + + var currentLayer = dupObj.artLayers[i]; + currentLayer.visible = true + + // Since we already save opacity, we dont want it affecting the output image + var opacity = currentLayer.opacity / 100.0; + currentLayer.opacity = 100; + + var layerName = dupObj.artLayers[i].name; // store layer name before change doc + var fileNameBody = layerName.toLowerCase(); + + // Ignore empty text layers + if (currentLayer.kind == LayerKind.TEXT && currentLayer.textItem.contents == "") continue; + + var documentCopyTmp = dupDocRef.duplicate(); + + // Trim empty space + if (activeDocument.activeLayer.isBackgroundLayer == false) { + app.activeDocument.trim(TrimType.TRANSPARENT); + } + + fileNameBody = fileNameBody.replace(/[ :\/\\*\?\"\<\>\|#]/g, "_"); // '/\:*?"<>|' -> '_' + if (fileNameBody.length > 120) { + fileNameBody = fileNameBody.substring(0, 120); + } + + var isText = (currentLayer.kind == LayerKind.TEXT && !(mainDialog.rasterizeText.value)) + + // Write QML properties + if (isText) qmlfile.write(" Text {\n"); + else qmlfile.write(" Image {\n"); + var filename = fileNameBody + ".png"; + qmlfile.write(" id:" + fileNameBody + "\n"); + + var xoffset = currentLayer.bounds[0].as("px"); + var yoffset = currentLayer.bounds[1].as("px"); + + if (isText) { + var textItem = currentLayer.textItem; + qmlfile.write(" text:\"" + textItem.contents + "\"\n"); + + // ### Temporary hack to set font positioning + // Using pointsize doesnt work for some reason and we need to + // figure out which metric we need to use ascending, perhaps? + yoffset -= textItem.size.as("px") / 4; + + qmlfile.write(" font.pixelSize:" + Math.floor(textItem.size.as("px")) + "\n"); + + //var fontfamily = app.textFonts.getByName(textitem.font); + qmlfile.write(" font.family:\"" + textItem.font + "\"\n"); + + if (textItem.font.indexOf("Bold") != -1) qmlfile.write(" font.bold:true\n"); + + if (textItem.font.indexOf("Italic") != -1) qmlfile.write(" font.italic:true\n"); + + qmlfile.write(" color:" + qtColor(currentLayer.textItem.color) + "\n"); + qmlfile.write(" smooth:true\n"); + } else { + qmlfile.write(" source:\"images/" + filename + "\"\n"); + } + + qmlfile.write(" x:" + xoffset + "\n"); + qmlfile.write(" y:" + yoffset + "\n"); + qmlfile.write(" opacity:" + opacity + "\n"); + qmlfile.write(" }\n"); + + // Save document + if (!isText) { + var saveFile = new File(exportInfo.destination + "\\images\\" + filename); + pngSaveOptions = new PNGSaveOptions(); + pngSaveOptions.interlaced = false; + dupObj.saveAs(saveFile, pngSaveOptions, true, Extension.LOWERCASE); + } + + // Close tempfile + documentCopyTmp.close(SaveOptions.DONOTSAVECHANGES); + } + + for (var i = 0; i < dupObj.layerSets.length; i++) { + var fileNameBody = fileNamePrefix; + fileNameBody += "_" + "s"; + exportChildren(dupObj.layerSets[i], orgObj.layerSets[i], exportInfo, dupDocRef, fileNameBody); // recursive call + } +} diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..0fb3c6c --- /dev/null +++ b/README.txt @@ -0,0 +1,16 @@ +Layers are exported as image and text elements in the root.qml file +and png images are dumped into the "images" subirectory. Note that +files may be replaced without warnings. + +Known issues: +The font names are not really mapped accurately at the moment. + +Notes: +The script has only been tested on Photoshop CS 4-5 and may or may not work on other versions. + +Warning: +The software is provided as is and NO warranty is given. Use at your own risk. + + +To install, just double click on the file or copy the script into your photoshop scripts directory. +This is usually located in Photoshop_path\Presets\Scripts. \ No newline at end of file diff --git a/example.psd b/example.psd new file mode 100644 index 0000000..3251062 Binary files /dev/null and b/example.psd differ diff --git a/example_output/MyElement.qml b/example_output/MyElement.qml new file mode 100644 index 0000000..521db9b --- /dev/null +++ b/example_output/MyElement.qml @@ -0,0 +1,118 @@ +import Qt 4.7 +Rectangle { + width:400 + height:255 + Image { + id:background + source:"images/background.png" + x:-2 + y:0 + opacity:1 + } + Image { + id:new_layer_1 + source:"images/new_layer_1.png" + x:1 + y:64 + opacity:0.2 + } + Image { + id:new_layer + source:"images/new_layer.png" + x:63 + y:161 + opacity:1 + } + Image { + id:new_layer_copy + source:"images/new_layer_copy.png" + x:136 + y:12 + opacity:1 + } + Text { + id:push_me + text:"Push me" + font.pixelSize:22 + font.family:"Tahoma" + color:Qt.rgba(255,255,255, 1.0) + smooth:true + x:164 + y:17.5 + opacity:1 + } + Image { + id:drop_shadow_2 + source:"images/drop_shadow_2.png" + x:22 + y:75 + opacity:0.3843137254902 + } + Image { + id:background_text + source:"images/background_text.png" + x:24 + y:77 + opacity:1 + } + Image { + id:drop_shadow_1 + source:"images/drop_shadow_1.png" + x:296 + y:158 + opacity:0.8 + } + Image { + id:mybutton + source:"images/mybutton.png" + x:309 + y:168 + opacity:1 + } + Image { + id:new_layer_copy_1 + source:"images/new_layer_copy_1.png" + x:116 + y:145 + opacity:0.63921568627451 + } + Image { + id:drop_shadow + source:"images/drop_shadow.png" + x:139 + y:112 + opacity:0.8 + } + Image { + id:new_layer_copy_2 + source:"images/new_layer_copy_2.png" + x:159 + y:121 + opacity:0.63921568627451 + } + Image { + id:drop_shadow_3 + source:"images/drop_shadow_3.png" + x:315 + y:194 + opacity:0.57254901960784 + } + Text { + id:button + text:"Button" + font.pixelSize:30 + font.family:"Tahoma" + color:Qt.rgba(255,255,255, 1.0) + smooth:true + x:187 + y:128.5 + opacity:1 + } + Image { + id:qml + source:"images/qml.png" + x:319 + y:197 + opacity:1 + } +} diff --git a/example_output/images/background.png b/example_output/images/background.png new file mode 100644 index 0000000..12e2dee Binary files /dev/null and b/example_output/images/background.png differ diff --git a/example_output/images/background_text.png b/example_output/images/background_text.png new file mode 100644 index 0000000..c9abd58 Binary files /dev/null and b/example_output/images/background_text.png differ diff --git a/example_output/images/drop_shadow.png b/example_output/images/drop_shadow.png new file mode 100644 index 0000000..3b8c456 Binary files /dev/null and b/example_output/images/drop_shadow.png differ diff --git a/example_output/images/drop_shadow_1.png b/example_output/images/drop_shadow_1.png new file mode 100644 index 0000000..da946db Binary files /dev/null and b/example_output/images/drop_shadow_1.png differ diff --git a/example_output/images/drop_shadow_2.png b/example_output/images/drop_shadow_2.png new file mode 100644 index 0000000..0e0a6b5 Binary files /dev/null and b/example_output/images/drop_shadow_2.png differ diff --git a/example_output/images/drop_shadow_3.png b/example_output/images/drop_shadow_3.png new file mode 100644 index 0000000..c738181 Binary files /dev/null and b/example_output/images/drop_shadow_3.png differ diff --git a/example_output/images/mybutton.png b/example_output/images/mybutton.png new file mode 100644 index 0000000..03bb59d Binary files /dev/null and b/example_output/images/mybutton.png differ diff --git a/example_output/images/new_layer.png b/example_output/images/new_layer.png new file mode 100644 index 0000000..334baff Binary files /dev/null and b/example_output/images/new_layer.png differ diff --git a/example_output/images/new_layer_1.png b/example_output/images/new_layer_1.png new file mode 100644 index 0000000..194002a Binary files /dev/null and b/example_output/images/new_layer_1.png differ diff --git a/example_output/images/new_layer_copy.png b/example_output/images/new_layer_copy.png new file mode 100644 index 0000000..bb14ea0 Binary files /dev/null and b/example_output/images/new_layer_copy.png differ diff --git a/example_output/images/new_layer_copy_1.png b/example_output/images/new_layer_copy_1.png new file mode 100644 index 0000000..13c2d00 Binary files /dev/null and b/example_output/images/new_layer_copy_1.png differ diff --git a/example_output/images/new_layer_copy_2.png b/example_output/images/new_layer_copy_2.png new file mode 100644 index 0000000..2ed699f Binary files /dev/null and b/example_output/images/new_layer_copy_2.png differ diff --git a/example_output/images/qml.png b/example_output/images/qml.png new file mode 100644 index 0000000..aa82e44 Binary files /dev/null and b/example_output/images/qml.png differ -- cgit v1.2.3