📜 ⬆️ ⬇️

We play the sapper in Photoshop



By the nature of my work, I occasionally have to automate my work in Photoshop. More precisely, I could not do this, but natural laziness leaves no chance in the fight against the routine, as they say "it is better to lose an hour, but then in 5 minutes to fly." Everything probably would have remained at the level of separate disparate scripts if it were not for the post from enotus . Thanks to him, I learned that you can write extensions to HTML + JS for Photoshop (as well as other products from Adobe). And went, gone.

It so happened that I usually start learning everything new by writing a simple toy on this newest one. For Photoshop, I chose Minesweeper. In this example, I would like to talk about creating an extension interface, interacting with photoshop, and handling events. So who is still interested, please roll.

As already described in my predecessor’s article , we’ll need Brackets with an installed extension and, well, Photoshop itself.
')
I also recommend immediately stocking up documentation from the Adobe page , most likely, you will need Photoshop CC JavaScript Reference and, possibly, Photoshop CC Scripting Guide. They are not directly related to the writing of extensions, but will be required to interact with Photoshop.

Getting started. Create a project in Brackets using the extension:



We will have a folder with a full set of files for the extension to work:


In the CSS folder, I added an additional file for the topcoat light theme. I prefer to work with the light interface, but this is not necessary. By the way, about Topcoat: you can download and read the documentation here .

Manifest can not touch, for the latest version of Photoshop everything is set up. If you need to change the extension icon or allow work in Photoshop of an earlier version, check these points in the documentation from the manufacturer .

In the js folder, we are interested in main.js, in fact, the main file of our project. But before moving on to it, we need to create an interface for our extension. This is done using HTML in the index.html file.

There is one more important file that I didn’t mention: hostscript.jsx. The JSX script is just what we’ve been using for quite some time to automate various processes in Photoshop and other Adobe projects. They even have a special utility ExtendScript Toolkit, it can also be useful, for example, for debugging jsx scripts.

index.html


In this file, too, everything is already prepared, a dark topcoat theme is connected, all the necessary scripts are added. Actually, all that is required is to fill the div # content:

<div id="content"> <div> :<br> <input type="text" id="w" class="topcoat-text-input" placeholder="width" value="6"> x <input type="text" id="h" class="topcoat-text-input" placeholder="height" value="6"><br>  :<br> <input type="text" id="m" class="topcoat-text-input" placeholder="width" value="8"><br> <button id="btn_start" class="topcoat-button--large hostFontSize">New game</button> </div> <hr> <div> <h2> : <span id="res">-</span></h2> <h3><span id="timer">0</span> sec.</h3> <button id="btn_check" class="topcoat-button--large hostFontSize" disabled>Check cell</button> </div> <div> <button id="btn_mark" class="topcoat-button--large hostFontSize" disabled>Mark</button> <button id="btn_unmark" class="topcoat-button--large hostFontSize" disabled>UnMark</button> </div> </div> 


As a result, we get the following picture:



You can go to JS.

main.js


In fact, there is already an example of the simplest use:

 /*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */ /*global $, window, location, CSInterface, SystemPath, themeManager*/ (function () { 'use strict'; var csInterface = new CSInterface(); function init() { themeManager.init(); $("#btn_test").click(function () { csInterface.evalScript('sayHello()'); }); } init(); }()); 

The most important here is the line csInterface.evalScript ('sayHello ()'); It is in this way that the interaction with the application occurs. The fact is that the extension mechanism is universal and does not depend on the application in which it is executed, but for the same reason he doesn’t know anything from the functionality — he is still a photoshop who is an illustrator. This is what JSX scripts are used for. And sayHello () is only one of the methods implemented in hostscript.jsx.

But, we also need not only to transfer data to jsx, but also to get results. Therefore, you can use the callBack function, including an anonymous one:

 csInterface.evalScript('checkCell()', function(result){ if(result<=0) { stopGame(); } else { $("#res").html(result); //      } }); 

This is how I check cells for mines: result contains the number of cells that still need to be opened to win. Mine corresponds to -1. If you noticed, jQuery is immediately connected among the scripts. So interacting with the interface is easy.

The only time that requires separate mention in the framework of main.js is a reaction to events occurring in the application outside our extension. On the one hand, everything is simple, on the other hand, worse than we would like. First of all I tried to catch a mouse click in the document - it was not there, there is simply no such event. As it turned out later, events relate, rather, to the entire application, and not to the document and have the character of “filter used”, “canceled last action” and the like. In addition, all events have a 4-letter identifier and a long numeric one, and in our case it is the long numeric that is needed, and only short codes are indicated in the documentation. Fortunately, each application has a method for converting one identifier into another, but it works within the framework of a jsx script.

In general, I will not ship you the details now, let's use an example from the documentation:

  function registerPhotoshopEvent(in_eventId) { var event = new CSEvent("com.adobe.PhotoshopRegisterEvent", "APPLICATION"); event.extensionId = csInterface.getExtensionID(); event.appId = csInterface.getApplicationID(); event.data = in_eventId csInterface.dispatchEvent(event); } csInterface.addEventListener("PhotoshopCallback" , function(event) { csInterface.evalScript("return (app.documents.length > 0)", stopGame) }); var closeEventid = "1131180832"; //    registerPhotoshopEvent(closeEventid); 


hostscript.jsx


As I wrote earlier, this is the main way to interact with the application, in this case with Photoshop CC. This is where the downloaded at the very beginning of the Photoshop CC JavaScript is useful. It contains a description of the properties and methods of various objects of Photoshop.

I will give here just a couple of examples. Start by creating a document:

 var startRulerUnits = app.preferences.rulerUnits; //     preferences.rulerUnits = Units.PIXELS; //     //  mainDoc = app.documents.add(width, height, dpi, "Game", NewDocumentMode.RGB); 

You know perfectly well that the sizes in Photoshop can be set in different measurement systems. Accordingly, you will receive the measurement result in the same units. Therefore, this should be closely monitored. In order not to get an unpleasant surprise, it is better to err and convert the units of measurement to the desired value.

Please note that there are exceptions. For example, the selected area is always set and measured only in PIXELS. And for example, all vector objects are measured exclusively in POINTS.

To convert units of measurement, I used a small peep method somewhere on the Internet:

 function convertValue(value, from, to) { output = new UnitValue(value, from); return output.as(to); } 

But here you will find a surprise. Conversion from mm and inches to pixels always occurs at the rate of 72 dots per inch, and if you have a different image resolution, you should recalculate yourself.

Well, perhaps the last example. A lot of things that we are accustomed to do in Photoshop as a single action programmatically need to implement a variety of commands. For example, for gluing layers in different ways (gluing everything, only visible, related) you need to implement yourself. From the available there is only an option to glue with the previous layer, and then your business (to check visibility, connectedness, swap layers). A similar situation with the selection of the area. You cannot select a single pixel circle or column. All that you offer to shove an array of coordinates of points, and what kind of a figure it will only depend on you:

 function DrawBomb(layer, x, y, colorHEX) { var startRulerUnits = app.preferences.rulerUnits; //    preferences.rulerUnits = Units.PIXELS; //     mainDoc.activeLayer = layer; mainDoc.selection.select([ [(x+0.2)*cellSize+padding, (y+0.2)*cellSize+padding], [(x+0.8)*cellSize+padding, (y+0.2)*cellSize+padding], [(x+0.8)*cellSize+padding, (y+0.8)*cellSize+padding], [(x+0.2)*cellSize+padding, (y+0.8)*cellSize+padding], [(x+0.2)*cellSize+padding, (y+0.2)*cellSize+padding] ]); var color = new SolidColor; color.rgb.hexValue = colorHEX; mainDoc.selection.fill(color); mainDoc.selection.deselect(); preferences.rulerUnits = startRulerUnits; //   } 


You can play


Well, the interface is drawn, the handlers are written in js, the logic in jsx is implemented, and you can play.
Do not forget to enable Debug Mode so that we can see our unsigned extension in Photoshop.



Now you can run Photoshop and activate our extension:



Well now you can play. True, due to the fact that the clicks on the image are not caught, you need to move the red rectangle and press the action buttons on the expansion panel.



Well, the process video:



Finally, I will say a few words about signing your extensions.
Do not forget to delete all hidden files before signing.
In Windows, the path to the folder with the extension must start from a dot, like this:
ZXPSignCmd.exe -sign ./ru.mobak.habrasample ru.mobak.habrasample.zxp cert.p12 pass

And please do not judge strictly my code, after all, I am not a programmer.

Files


Expansion sources can be downloaded here .

Ready extension that can be installed via Adobe Extension Manager is here . Signed with a self-made certificate, so that it poured a little.

PS I remembered that I forgot to write about one excellent resource, though in English, but with a bunch of examples: http://www.davidebarranca.com/

PPS Another point that I wanted to say. Previously, to update the extension, you had to close and restart Photoshop. But progress does not stand still, now it is enough to minimize and expand the expansion panel. Thus, you can watch the result literally even after each change.

Source: https://habr.com/ru/post/253337/


All Articles