![]() |
Adobe Photoshop SDK |
|
Scripting Plug-insSections on this Page
Adobe Photoshop 4.0 introduced a new palette and subsequent set of commands and callbacks: the Actions palette, and the Descriptor callback suite. The Actions palette is the user-interface and hub for the scripting system for Adobe Photoshop. Adobe Photoshop 5.0 extends the Actions structure to include automation plug-ins that can access all scriptable Photoshop commands. Actions allow commands in Photoshop to be recorded in a form that is easy for an end user to read and edit. Actions are similar to AppleScript and AppleEvents but are cross platform and designed to support both AppleScript on the Macintosh and OLE Automation on Windows. Actions extend the plug-in API to allow Import, Export, Filter, Format and Selection plug-in modules to be fully recordable and automated. Scripting on Windows with OLE AppleScript and AppleEvents recommended reading
See MacTech develop Article Archives. All the plug-in module examples that support scripting have been updated. Detailed code-related information is available in each separate module example and in PIActions.h. Photoshop 5.0 Automation plug-ins Scripting BasicsFor a plug-in to be scripting-aware, or able to record scripting parameters and be automated by them, it requires the addition of two basic mechanisms:
Implementation OrderWe recommend you convert existing plug-ins to scripting-aware plug-ins by following this scripting implementation order:
Scripting CaveatsThe scripting system has been designed specifically to drive plug-ins in a way that is transparent to the existing operation of the host. This means that there is no way to know whether your plug-in is being driven by the scripting system or an end-user. You should treat all operations as consistently as possible. The scripting system always hands the plug-in a descriptor at every selector call. If the plug-in uses a descriptor that was handed to it by the host, and it hands back a new descriptor, the plug-in is responsible for deleting the old descriptor. All the examples do this through the set of utility routines in PIUtilities. If the plug-in doesn’t use the descriptor handed to it by the host, the plug-in can hand it back and it will be deleted automatically. If the plug-in doesn’t use the descriptor handed to it by the host, but the plug-in hands back NULL, then the plug-in is responsible for deleting the descriptor the host handed it. Creating a Terminology ResourceA terminology resource is used to specify the mapping from a descriptor to human readable text. The format of the terminology resource is identical to an AppleEvent terminology resource. For further information on the 'aete' resource, see "Making Sense of 'aete' Resources" (Richard McGath, MacTech, Volume 11, issue 7), found in the MacTech Article Archives. To let Photoshop know that the terminology resource is present, a PiPL property is added, The terminology resource is a complex structure designed by Apple to cover numerous scripting situations that are not required by Photoshop. By that nature, the structure is more complicated than it needs to be to describe plug-ins. However, it was chosen because Apple plans to support it both now and in the future, and it allows you to increase the scope of your plug-in by being AppleEvent- and AppleScript-savvy. See AppleScript Compatibility. Basic Terminology Resource Formatresource 'aete' (0) { // aete version and language specifiers { // suite descriptor { // filter/selection/color picker descriptor { // any parameters // additional parameters } }, { // import/export/format descriptors { // properties. First property defines inheritance. // any properties }, { // elements. Not supported for plug-ins. }, // class descriptions for other classes used as parameters or properties }, { // comparison ops. Not currently supported. }, { // any enumerations { // additional values for enumeration }, // any additional enumerations // variant types are a special enumeration: { // additional types for variant }, // any additional variants // class and reference types are a special enumeration: { }, // any additional class or reference types } } } Whether your plug-in is a filter, or one of the others, each section of the terminology resource must be present (even if it is blank Detailed Terminology Resource Exampleresource 'aete' (0) { 1, 0, english, roman, // aete version and language specifiers { // suite descriptor below "suite name", // name of suite "description", // optional description of suite 'stID', // suite ID, must be unique 4-char code 1, // suite code, must be 1 1, // suite level, must be 1 { // filter/selection/color picker descriptor below "plug-in name", // name of plug-in, must be unique "description", // optional description of filter 'clID', // class ID, must be unique // 4-char code or suite ID 'evID', // event ID, must be unique 4-char //code within class (may be suite ID) NO_REPLY, // never a reply IMAGE_DIRECT_PARAMETER // direct parameter { // any parameters below "parameter name", // name of parameter 'kyID', // parameter key ID. 'tyID', // parameter type ID. flagsTypeParameter, // parameter flags. // additional parameters here } }, { // import/export/format descriptors below "plug-in name", // name of plug-in, must be unique 'clID', // class ID, must be unique // 4-char code or suite ID "description", // optional description of plug-in { // properties below. First property defines inheritance. "<Inheritance>", // required keyInherits, // required classInherited, // parent class: Format, Import, or Export "", flagsSingleProperty, // any properties below "property name", // name of property 'kyID', // property key ID. 'tyID', // property type ID. "description", // optional description flagsTypeProperty, // property flags. }, { // elements. Not supported for plug-ins. }, // class descriptions for other classes used as parameters or properties }, { // comparison ops. Not currently supported. }, { // any enumerations below 'enID', // enumeration ID { "enumerated name", // first value name 'e1ID', // first value ID "description", // optional description of first value // additional values for this enumeration }, // any additional enumerations // variant types are a special enumeration: '#vID', // variant ID (must begin with "#") { "type name", // first type name 'v1ID', // first type ID "", // additional types for variant }, // any additional variants // class and reference types are a special enumeration: '#tID', // enumeration ID (must begin with "#") { "type name", // name of type 't1ID', // type ID. Either typeClass or // typeObjectReference. "" }, // any additional class or reference types } } } NomenclatureThe user terms in the terminology resource should be all lower case with the exception of proper names and acronyms. Photoshop will capitalize terms appropriately. Parameters and PropertiesThe terminology resource has two basic ways to describe information a Photoshop plug-in needs to provide scripting. For Filter, Selection and Color Picker plug-ins, parameters generally provide keys for an event (see Filter, Selection, and Color Picker Events). They usually describe input that a plug-in receives from the user through the plug-in user interface. For Import, Export and Format plug-ins, input from the user is structured through class definitions, which are In the terminology resource, both parameters and properties are defined by four basic pieces of information:
Parameter and Property FlagsParameter and property flags provide information about how parameters and properties are used. Parameter flags indicate,for example, whether the parameter takes single or multiple values, or whether its value comes from an enumeration. See the Similarly, property flags indicate, for example, whether an object can have one or more of a particular property, or whether its value comes from an enumeration. See the The Photoshop SDK predefines flags for parameters and properties, which make coding of the 'aete' resource somewhat simpler. See parameter flags and property flags. The flags for properties are the same for parameters, except there is not a flag for optional properties. Properties can be optional by putting "optional" at the beginning of the description field.
Classes and the Terminology ResourceFor Import, Export, and Format plug-ins, the first property in the terminology resource must indicate inheritance ( The Photoshop SDK provides a number of "predefined classes" that are available for use in the terminology resource. A useful subset of those classes is shown in the table below. Use these classes when they are appropriate, but you can define new classes in the terminology resource, if necessary. See the
Class InheritanceThe Inheritance property (
Class types are defined by creating a special enumeration. For example, the class { /* suite descriptor below */ "color", // class name classColor, // class ID for Color 'Clr ' "", // no description { "color", // color property (special for base class) keyColor, // property ID for Color 'Clr ' typeClassColor, // type this class "", // no description flagsEnumeratedParameter // "type" is special enumeration }, { /* no elements */ } ... } The class "RGB color", // class name classRGBColor, // class ID 'RGBC' "", // no description { "<Inheritance>", // define inheritance keyInherits, // property ID for inheritance 'Clr ' classColor, // from parent class "color" "", // no description flagsSingleParameter // single parameter "red", // red property keyRed, // property ID for Red 'Rd ' typeFloat, // value type "float" "", // no description flagsSingleParameter // single parameter "green", // green property keyGreen, // property ID for Green 'Grn ' typeFloat, // value type "float" "", // no description flagsSingleParameter // single parameter "blue", // blue property keyBlue, // property ID for Blue 'Bl ' typeFloat, // value type "float" "", // no description flagsSingleParameter // single parameter }, { /* no elements */ } Enumerated TypesEnumerated types are used in the standard fashion to create a type that can have one or a set of values. See
For example, an enumerated type for quality with the values of low, medium, high, and maximum is defined: typeQuality, // type ID for Quality 'Qlty' { "low", // "low" value enumLow, "", "medium", // "medium" value enumMedium, "", "high", // "high" value enumHigh, "", "maximum", // "maximum" value enumMaximum, "", } Variant typesEnumerated types are also used to specify variant types for parameters and properties.
If you have a parameter that can take text or an integer, it is defined: "specifier", // parameter name keySpecifier, // parameter ID typeTextInteger, // text or integer "index or name", // short description flagsEnumeratedParameter Where In the PiPL resource file, the type typeTextInteger, // type ID (variant types must begin with "#") { "string", // name of first type (AppleScript name) typeText, "", "integer", // name of second type typeInteger, "" } Enumerations and object reference types Enumeration variants can also be used to specify object reference types and class types. From the example of the class color, typeClassColor is defined: typeClassColor, // type ID (variant types must begin with "#") { "type color", // name of type typeClass, // generic type reference "", } Lists and the Terminology ResourceAll types can be used as lists for parameters and properties. All items in a list must be of the same type. To specify a list in the terminology resource use the flagsListParameter or flagsListProperty. DescriptorsBecause the Actions palette provides an alternate, text-based user interface to Adobe Photoshop, textual script commands need to map intuitively to the graphical user interface. The way to start developing a scripting interface for your plug-in is to look carefully at the options provided in your dialogs, and then describe them in writing. For some options, such as checkboxes and popup menus, this is fairly straight-forward. For others, such as showing placement of an object graphically, this is more difficult. All scripting commands are described with the following form within the Actions palette. This form provides a descriptor for the event.
Filter, Selection, and Color Picker EventsFilter, Selection, and Color Picker scripting is described as "scripting events". These events have a descriptor syntax in the Actions palette with the following form:
Such as:
Import, Export, and Format ObjectsImport, Export, and Format scripting is described as "scripting objects". These objects have a descriptor syntax in the Actions palette with the following form:
Such as:
In the example
Save as objectTo save as an object, the nomenclature is
Save as typeSaving as a type takes the form of
Type typeObjectReferenceThe type When using the Action Descriptor Suite, the read and write functions An object reference can refer to an object which is either an element or a property of another object. Elements may be referred to by name or index. Plug-ins can only refer to elements or properties of the immediate target, due to the one-dimensional nature of
But it cannot specify:
If a plug-in attempts to read a complex object reference (for instance, one containing other references) the host attempts to simplify the reference; if it can’t, it returns an error. Scripting ParametersOnce you’ve added a terminology resource and you’ve edited the At every selector call, the host passes the plug-in a descriptor structure through the Descriptor Suite portion of the parameter block: The plug-in can access the data structure in the See the Hidden sample for an example of converting the RecordingBuilding a descriptor If your plug-in has no options, To build a descriptor using the Action Descriptor Suite:
The following example of building a descriptor using the Action Descriptor Suite is from the PIActionDescriptor actionDescriptor = NULL; // Create the action descriptor HERROR(sPSActionDescriptor->Make(&actionDescriptor)); // Put the descriptor key/value pairs into the descriptor HERROR(sPSActionDescriptor->PutEnumerated(actionDescriptor, keyResult, typeResult, enumResult)); // See if we need to clear the handle of an existing descriptor if (filterRecord->descriptorParameters != NULL && filterRecord->descriptorParameters->descriptor != NULL && filterRecord->handleProcs != NULL) { HandleProcs * handleProcs = filterRecord->handleProcs; handleProcs->disposeProc(filterRecord->descriptorParameters->descriptor); filterRecord->descriptorParameters->descriptor = NULL; } // Convert the Action Descriptor to a PIDescriptorHandle, and return // it in the descriptor parameters structure. HERROR(sPSActionDescriptor->AsHandle(actionDescriptor, &filterRecord->descriptorParameters->descriptor)); // Free the action descriptor if (actionDescriptor != NULL) sPSActionDescriptor->Free(actionDescriptor); To build a descriptor using the Deprecated Standard Descriptor Suite:
Recording Error HandlingIf an error occurs during or after Recording ClassesPlug-ins can declare classes to use as templates for structures. Classes declared by plug-ins cannot contain elements, but can use inheritance. Objects of a particular class are created by defining a descriptor and adding the key/value pairs for the properties. The root property of the base class is not added to the descriptor. PlaybackReading a descriptor To read a descriptor using the Action Descriptor Suite:
The following example of reading a descriptor using the Action Descriptor Suite is from the // Get the descriptor handle from the parameter block PIDescriptorHandle descHandle = NULL; descHandle = filterRecord->descriptorParameters->descriptor; popDialog = !(filterRecord->descriptorParameters->playInfo == plugInDialogSilent); // Convert the descriptor handle into an Action Descriptor PIActionDescriptor actionDescriptor = NULL; sPSActionDescriptor->HandleToDescriptor(descHandle, &actionDescriptor); Boolean hasKey; uint32 length; // Call HasKey for each key we expect, followed by the appropriate Get routine. error = sPSActionDescriptor->HasKey(actionDescriptor, keyMyNudgeH, &hasKey); if (!error && hasKey) sPSActionDescriptor->GetFloat(actionDescriptor, keyMyNudgeH, &fBigNudgeH); // ... error = sPSActionDescriptor->HasKey(actionDescriptor, keyMyWatch, &hasKey); if (!error && hasKey) sPSActionDescriptor->GetInteger(actionDescriptor, keyMyWatch, &fWatchSuspension); // Free the action descriptor if (actionDescriptor != NULL) sPSActionDescriptor->Free(actionDescriptor); } To read a descriptor using the Deprecated Standard Descriptor Suite:
Playback error handlingBecause a descriptor can be built by other software, don’t assume that your keys will come in order, be of the proper type, or all be present. This is only relevant for plug-ins using the Deprecated Standard Descriptor Suite, in which keys are retrieved in order from the descriptor using the Coerced ParametersIf a If an error occurs and not
Sticky ErrorsErrors that occur in
DescriptorKeyIDArrayWhen using the Deprecated Standard Descriptor Suite, during the repeated calls to AppleScript CompatibilityThe Photoshop scripting system was made with AppleScript compatibility as one of its primary goals. This explains the reliance on some of the more (seemingly needless) complex structures such as the dictionary resource. The reliance on the dictionary resource, and the structure of the key name and ID pairs, maps directly to AppleScript. There are some important details to watch out for. AppleScript maintains a global name space, which means if your plug-in is going to be AppleScript compatible, your key name and ID pairs must be completely unique. For example, if you defined: "red", // red property myRed, // my unique property ID for Red typeFloat, // value type "float" "", // no description flagsSingleParameter // single parameter "green", // green property myGreen, // my unique property ID for Green typeFloat, // value type "float" "", // no description flagsSingleParameter // single parameter "blue", // blue property myBlue, // my unique property ID for Blue typeFloat, // value type "float" "", // no description flagsSingleParameter // single parameter You would ruin "red", "green", and "blue" for anyone else who attempted to use it, as it would now map to your unique keys (or whoever got their dictionary registered before yours.) In this case, you must use unique textual names as well, such as: "AdobeSDK red", // unique red property name myRed, // my unique property ID for Red typeFloat, // value type "float" "", // no description flagsSingleParameter // single parameter In that case, future requests would take the form of: tell "Adobe SDK dissolve" set AdobeSDK red of AdobeSDK Dissolve to 65535, 0, 0 end tell This way is safe and makes sure you don’t conflict with anything else. When in doubt, make the name and ID unique, or use the predefined values. Those are always available and are mapped to your plug-in through your dictionary resource automatically. Registration and Unique Name SpacesWhen trying to determine unique key name and ID spaces, you must follow these rules:
Since the scripting system is based on unique IDs and AppleScript, and works the same between Mac OS and Windows, this means that if you wish to register a unique ID you must use the Apple filename ID registration web page, whether Windows or Mac OS based. The web page is at http://dev.info.apple.com/cftype/main.html. Common Adobe Plug-in ID Good Will FormatFor all plug-in developers wishing to allocate a block of IDs (which is common to want to do, for sets of plug-ins needing unique variables, etc.) register your plug-in type as a variant, with the first three characters following the basic rules for ID creation, and a last character of "#". This registers all 255 permutations of your ID. For example,
Remember, if you’re registering a block of name space, that the first three characters must follow the ID rules: they must start with a lowercase character, and at least one character must be uppercase.
When you log onto the registration web page to check your unique ID, you must check for 'nam#' where nam is your three-digit ID. If you check and register any four digit ID, without searching for your three-digit ID + "#", then you will probably stomp someone else's name space. This name space registration method is only useful if we all agree do follow it. Ignoring AppleScriptIf you’ve decided that forward compatibility with future AppleScript features is not a major concern, you can disable any AppleScript-savvy features and make your plug-in only Photoshop-specific. By doing this, you may ignore any requirements for unique key name and ID pairs. To do this, add a unique ID string to your For example, the following entry in the PiPL resource file ignores AppleScript: HasTerminology { plugInClassID, // Class ID plugInEventID, // Event ID ResourceID, // AETE ID // Unique string or empty for AppleScript compliant: "98b5a608-46ce-11d3-bd6b-0060b0a13dc4" } But the following entry indicates the plug-in is AppleScript compatible: HasTerminology { plugInClassID, // Class ID plugInEventID, // Event ID ResourceID, // AETE ID // Unique string or empty for AppleScript compliant: "" } For more information, see Scripting-specific PiPL Properties, and the AppleEventsIn Photoshop 4.0 and 5.0, besides the standard AppleEvents, there is one additional AppleEvent call that is supported by the host: You can call into Photoshop with the tell application "Photoshop 4.0" do script "MyAction" end tell |