Making Your Plug-In Scriptable

Transcription

Making Your Plug-In Scriptable
Making Your Plug-In
Scriptable
Technical Note #10082
Version InDesign CS/InCopy CS
11 Sep 2003
ADOBE SYSTEMS INCORPORATED
Corporate Headquarters
345 Park Avenue
San Jose, CA 95110-2704
(408) 536-6000
Copyright 2003 Adobe Systems Incorporated. All rights reserved.
The information in this document is furnished for informational use only, is subject to change without notice, and should not be construed as a
commitment by Adobe Systems Incorporated. Adobe Systems Incorporated assumes no responsibility or liability for any errors or inaccuracies that
may appear in this document. The software described in this document is furnished under license and may only be used or copied in accordance with
the terms of such license.
Adobe, Adobe After Effects, Adobe InDesign, Adobe PhotoDeluxe, Adobe Premiere, Adobe Photoshop, Adobe Illustrator, Adobe Type Manager,
ATM and PostScript are trademarks of Adobe Systems Incorporated that may be registered in certain jurisdictions. Macintosh and Apple are
registered trademarks, and Mac OS is a trademark of Apple Computer, Inc. Microsoft, Windows, Windows 95, Windows 98, and Windows NT are
registered trademarks of Microsoft Corporation. All other products or name brands are trademarks of their respective holders..
Rev #
Date
Author
Comments
0.1
09 Apr 2003
Ken Sadahiro
First draft for InDesign/InCopy 3.0 Beta SDK.
(Most of the 3.0 specific material was written
by Jonathan Brown.)
0.2
11 Apr 2003
Ken Sadahiro
Second draft.
0.3
14 Apr 2003
Ken Sadahiro
Third draft, corrected images, code
indentation, and spelling. Inserted new image
for Visual Basic Object Browser.
0.4a
23 May 2003
Ken Sadahiro
Fourth draft. Included review
notes/corrections/further comments from
Jonathan Brown and Steve Pellegrin.
1.0
14 Jul 2003
Ken Sadahiro
Updated the “InDesign CS” and “InCopy CS”
designations, reimported screen shots.
1.1
03 Sep 2003
Ken Sadahiro
Further updates based on review by Jonathan
Brown. Reflected changes between last prerelease version and latest builds.
1.2
11 Sep 2003
Ken Sadahiro
Added registry cleanup instructions for users
of Windows pre-release builds.
Contents
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Terminology and Definitions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
What is Scripting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
The Benefits in Making a Plug-in Scriptable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
What is Involved in Making a Plug-in Scriptable? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Necessary Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Architectural Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
General Description of the Scripting Process. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Interactions in the Scripting Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Example Plug-in: Custom Prefs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Recipes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
How to Add a New Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Exposing a New Property or Event on an Existing Object . . . . . . . . . . . . . . . . . . . . . . . 18
Defining IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Reviewing the New Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Supporting The InDesign Interchange File Format: Additional Info . . . . . . . . . . . . . . . . . 21
Verifying That Your New Script Objects Are Exported In The New InDesign Interchange File
Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Tips for Debugging the Scripting Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Converting Existing Scriptable Plug-ins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS . . . . 25
Converting Script Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
API Changes Between InDesign 2.0.x And InDesign CS . . . . . . . . . . . . . . . . . . . . . . . . . 31
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Resource Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
APIs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Appendix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
How did the Scripting Architecture Change from InDesign/InCopy 2.x to InDesign CS/InCopy CS?
52
Using The TypeInfoConverter Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Additional Resources For Further Understanding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
iii
Contents
iv
11 Sep 2003
Making Your Plug-In Scriptable
1
#10082 Making Your Plug-in Scriptable
Introduction
This technical note describes the benefits and the process of adding scripting support to an
InDesign CS/InCopy CS plug-in. This document is the programming counterpart to the
InDesign CS/InCopy CS Scripting Guide, which provides details on script programming
approaches and a script object reference. Both the Scripting Guide and this technical note are
recommended reading if you, as a plug-in developer, are considering a script-based solution.
This technical note also describes the relationship between scripting support and the new
InDesign Interchange file format in InDesign CS.
Terminology and Definitions
This section defines terms used within this document.
•
Apple Events; High-level, semantic messages designed to allow for collaboration between
programs.
•
Apple Event Terminology extension (AETE); A resource that defines Apple Events and
objects which your application understands, both for scripts and for applications that
send you events. Developed by Apple.
•
Component Object Model (COM); A distributed, object-oriented system for creating
binary software components that can interact with each other. Developed by Microsoft.
•
Object Description Language (ODL); A Windows-specific description about COM objects.
This is described in an .ODL file, and is compiled by the Microsoft Interface Definition
Language (MIDL) compiler. The output of the MIDL compiler is an COM type library
that is linked in with the plug-in as a resource.
•
Type Library; Binary files (.tlb files) that include information about types and objects
exposed by a COM application. A type library can contain any of the following: (1)
Information about data types, such as aliases, enumerations, structures, or unions. (2)
Descriptions of one or more objects, such as a module, interface, IDispatch interface
(dispinterface), or component object class (coclass). Each of these descriptions is
commonly referred to as a typeinfo. (3) References to type descriptions from other type
libraries.
•
JavaScript; A scripting language to automate the document object model within an
HTML browser, such as Netscape Navigator or Microsoft Internet Explorer.
•
Script; A series of commands that tell InDesign/InCopy to perform a series of actions.
These actions may only involve an item in an InDesign/InCopy document or may involve
#10082 Making Your Plug-in Scriptable
5
#10082 Making Your Plug-in Scriptable
What is Scripting
many documents and additional applications such as word processors, spreadsheets, and
databases.
•
Script object; Target of an event (method call), or property request issued by execution of
a user script.
•
Property; An attribute of the script object.
•
Event; A method call on a script object.
•
Host Application; The application that provides the script-based automation support. In
this technical note, this primarily refers to InDesign/InCopy.
•
Script provider; A boss that provides an implementation to find a script object, get and set
its properties, or execute its methods (called Events).
•
Script manager; Application-supplied managers represent supported languages,
AppleScriptMgr for AppleScript or OLEAutomationMgr for Visual Basic.
•
Script language; The language in which you write your scripts. For InDesign/InCopy,
three scripting languages are supported: AppleScript, Visual Basic and JavaScript.
•
InDesign/InCopy Document Object Model (DOM); Set of objects, properties, and events
that make up a document, including page items on the document hierarchy, and various
preferences.
•
InDesign/InCopy Object Model; Implementation view of the document—bosses,
interfaces, implementations.
What is Scripting
InDesign/InCopy allows users of the applications to automate layout and text editing tasks, by
means of executing instructions in a script, written in AppleScript, Visual Basic/COM, or
JavaScript. By playing back the script in the appropriate environment, such as AppleScript or
Visual Basic, you can perform tasks in an automated manner or with a customized interaction.
For the application user, scripting is useful when the task at hand is very repetitive or
cumbersome to perform. In some cases, scripting provides a convenient way to prototype an
automated and/or customized user interaction and in some cases may be considered a viable
alternative to plug-in development, when the task you want to automate can be achieved using
the functionality provided by the application (most of the application already comes with
scripting support built-in).
This technical note will not discuss how to write such scripts for InDesign/InCopy, but will
focus instead on how to extend your own plug-in so that its features can be automated by a
script. If you are looking for details on how to write scripts for InDesign/InCopy, please instead
refer to the Scripting Guide, which is available on the application's CD-ROM, and on the
internet at http://www.adobe.com/products/indesign/scripting.html.
NOTE: The rest of this technical note is targeted for the plug-in developer.
6
#10082 Making Your Plug-in Scriptable
What is Scripting
The Benefits in Making a Plug-in Scriptable
For the plug-in developer, the benefits are of making a plug-in scriptable are as follows:
•
InDesign/InCopy users can automate the features added by your plug-in by writing and
executing scripts.
•
You can seamlessly provide support for custom DOM objects and properties added by
your plug-in, when a user saves a document in the InDesign Interchange file format.
The potential for new solutions using automation with InDesign means added productivity for
you and your customers, as explained in the previous section.
In InDesign CS, if your plug-ins implement custom DOM objects, such as custom page items,
and additional persistent data, you can seamlessly augment the InDesign Interchange file
format simply by adding scripting support to your objects.
What is Involved in Making a Plug-in Scriptable?
The principal costs of adding scripting support for your plug-in are in:
•
Designing and implementing the addition of scripting objects, methods and properties. This
depends on how many scripting objects, methods and properties you need to implement.
By reading the Recipes section of this guide, this should take about a day or two.
•
Rearchitecting your code to fit into the scripting architecture. The time required for
rearchitecting depends on the complexity of your plug-in and how well separated your
user interface was from your model. This technical note does not make any suggestions
on how to refactor your own plug-in code, however, a common refactoring task may be to
decouple the user interface and model related source code in your plug-ins. A good way
to think of the scripting architecture is that it is another user interface.
•
Testing. Testing your scripting solution involves writing separate scripts on two platforms:
Macintosh and Windows. With the refactoring of the scripting architecture (especially in
the resource definitions) in InDesign CS/InCopy CS, the necessity for complete platformspecific testing has been reduced. While this technical note does not go into detail about
testing your scriptable plug-ins, you can refer to Reviewing the New Resources in the
Recipes section to learn how to verify that your scriptable plug-in does indeed expose the
scripting objects you intend.
•
Documenting your solution. Documenting your script provider depends on the
complexity of your plug-in. Also, the scripting language syntax differs by platform, so
naturally, so will your documentation. This is an exercise left to the reader of this
technical note.
Necessary Tools
To make your plug-in scriptable, you need the following tools:
#10082 Making Your Plug-in Scriptable
7
#10082 Making Your Plug-in Scriptable
Architectural Overview
•
The current integrated development environment (IDE) that you use for developing
plug-ins. Refer to the Readme file in the InDesign CS/InCopy CS SDK for the latest
supported IDEs and their versions.
•
An environment to develop and run scripts (also to verify that your scriptable plug-in is
exposing the right script objects)
—
Macintosh: AppleScript Editor
—
Windows: Microsoft Visual Basic, VBA/VBScript environment, or any COM
development environment (including Microsoft Visual C++)
—
Both platforms: JavaScript editor and environment
(Refer to Reviewing the New Resources in the Recipes section for steps on how to verify
your script objects.)
Architectural Overview
This section provides a general architectural description of scripting for InDesign/InCopy.
Details on how to create the necessary resource files and use the relevant interfaces are
contained in the section titled “Recipes”.
General Description of the Scripting Process
8
1.
Once the host application launches, the scripting system architecture support is available
to the user through scripts. However, nothing is activated until a scripting event is
received.
2.
When a scripting event is received, the script manager (either AppleScriptMgr for
Macintosh or OLEAutomationMgr for Windows) is responsible for figuring out what to
do with that event. Basically, after doing setup, checks, and so on, the script manager
populates the IScriptEventData interface with the data that has been passed in.
3.
The script manager invokes the IScriptRequestHandler via one of the basic entry points:
DoAccessProperty(), DoSetProperty(), DoHandleEvent(), or DoGetObject(), which in
turn invokes the appropriate script provider.
•
If a property comes in, it is the script service provider's responsibility to either get or
set the property based on the parameters passed in through the IScriptEventData
interface. If it is a get operation, the script provider simply returns the requested data
from the model. If it is a set operation, the script provider extracts the input and
either executes its code successfully (i.e. modifying the model) or returns an error.
•
Events work in a similar fashion. Some events don't require any data at all, but most
events require some set of parameters. The script provider uses the IScriptEventData
interface to get that input, executes any necessary model modification via
commands, and either returns data or an error code.
#10082 Making Your Plug-in Scriptable
Architectural Overview
4.
Then, control goes back to the script manager, which does cleanup and conversion. The
script manager is responsible for taking the data in the IScriptEventData interface and
converting it back into something the scripting language understands.
5.
Finally, the script manager returns, and the control is passed back to the script.
Interactions in the Scripting Architecture
Generally speaking, the scripting architecture, scripting languages and the object model all
interact to provide support for scripting. (For implementation details, refer to the Recipes
section.)
•
Interaction with Scripting Languages
InDesign supplies managers and interfaces to handle the interaction between your plugin and the scripting language. This section describes the pieces necessary to enable a plugin to receive a scripting event from the scripting manager and to report data back to the
scripting manager (step 3 in the ordered sequence above).
1.
Scripting Language Resources
One portion of the language interaction every scripting plug-in needs to supply is a
ScriptElementInfo resource (new in InDesign CS/InCopy CS). (NOTE: In
InDesign/InCopy 2.x, this used to be implemented in platform specific resources,
namely the AETE Resource for Macinotsh and ODL for Windows.) In this resource,
you identify details about objects, relationships with other objects, and map them to
unique four-character script IDs. (To discover the unique IDs reserved by the
application, see source/public/includes/ScriptingDefs.h).The contents of these
resources will be discussed in detail later in this document.
2.
IScript Interface
The IScript interface is used as a marker in a boss to identify a scriptable object. The
script object is the target of an event, method call, or property request issued by
execution of a user script. For example, the application-supplied Document object
is the target of the Save() method and of the Selection property. The need to
implement an IScript interface depends on whether you want to add functionality
to an application-supplied object, or are implementing one of your own. If you
want to add functionality to an application-supplied object, such as the Document,
you do not implement the IScript interface. If you are creating a new script object,
you must define a boss class that implements the IScript interface, which maps your
boss to the scripting object identified by its four-character ID. The details will be
discussed later in the document.
•
Interaction with the model
As with other user interfaces you would give a plug-in, scripting is intended to query and
change the model. To accomplish this, we must be able to identify objects in the hierarchy
DOM via IScript and get and/or set values. Furthermore, we must carefully design our
own additions to the model in the form of objects and properties. This section describes
#10082 Making Your Plug-in Scriptable
9
#10082 Making Your Plug-in Scriptable
Architectural Overview
the parts of the plug-in that enable the scripting manager to access and/or modify the
model (namely step3 in the ordered sequence above).
1.
Script provider boss base classes
When you implement a script provider boss, you extend kBaseScriptProviderBoss.
(Refer to the HTML-based Reference in the SDK to examine what interfaces and
implementation are aggregated on these bosses.)
2.
IScriptProvider
Supply the IScriptProvider interface implementation to help the script manager
find an object, get and set its properties, and execute its events. It is in the methods
of this interface where you change the model.
Example Plug-in: Custom Prefs
Let's examine one of the SDK sample plug-ins, CustomPrefs (a plug-in that adds a custom
prefs object of bool16 type to the workspace and document workspace bosses), to see how
scripting support is added to a plug-in that only provides the model, a user interface (in this
case, the preference panel) and a command to modify the model. A prefs object is a special
kind of script object.
•
Defining the Script Object
In defining your script object, you must first determine whether you need to create your
own script object, or use an existing script object, provided that a meaningful one exists.
(To see a list of the existing objects, look at ScriptClasses in ScriptingDefs.h and at
ScriptingWindowDefs.h in the SDK’s API directory.) In the CustomPrefs plug-in, we had
added a custom preference which requires a custom object, known as a Preferences
Object.
When you create a custom object, you must decide where you want to place it within the
DOM hierarchy. The CustomPrefs plug-in adds a preference to the kWorkspaceBoss and
kDocWorkspaceBoss bosses, which corresponds to the Application and Document
objects.
10
#10082 Making Your Plug-in Scriptable
Architectural Overview
Below is a screen shot of the Visual C++ OLE Type Library Browser. You can see the
Document object and its properties and events.
•
Properties and Events
Your script object may have methods and/or properties. The CustomPrefs plug-in adds a
single property called "pref value", which is a readable/writable bool16 value. The plug-in
adds no events (akin to methods in a C++ class), however, if we were to add one, say
"FlipValue( )", we would define it on the same object.
•
Summary of the New Script Object
You can prepare a table (like the one below) that shows for the methods and properties of
each object your plug-in will support. In it, you define method and property names,
decide on data types, read-write access, and finally provide a description of the method or
property.
TABLE 1.1 Defining your new objects, properties, and events
DOM Object
Custom Preference
New object?
Yes
Parent
object(s)
Application, Document
Collection?
No
Properties
pref value: Boolean (read/write)
Events
n/a
Description
The custom preference property value added in by the CustomPrefs plug-in.
#10082 Making Your Plug-in Scriptable
11
#10082 Making Your Plug-in Scriptable
Architectural Overview
•
Defining the ScriptElementInfo Resource
The next step is to define the ScriptElementInfo resource as an ODFRC resource, so that
the script manager can add our object to the DOM where we want it. We would write this
resource info in a .fr file. The necessary parts to this resource for a Preferences Object are
as shown below.
—
Object: Defines our custom object.
—
Property: One for the custom object itself, as it will be considered a property of the
Application and Document objects, and one for the boolean value.
—
Provider: Defines where the custom object will be located in the DOM, and specifies
the script provider boss.
The following ScriptElementInfo resource can be found in
{SDK}/source/samplecode/customprefs/CstPrf.fr. In the ScriptElementInfo resource
below, the IDs we need to define are denoted by italicized text. (Comments have been
removed from the example below for brevity. Descriptions of each of the fields can be
found in the "References" section, later in this technical note.)
resource ScriptElementInfo(1)
{
kCoreScriptManagerBoss,
kWildFS, k_Wild,
{
Object
{
kCstPrfObjectScriptElement,
c_CustomPref,
"Custom preference",
"The Custom preferences object....",
kCPrefs_CLSID,
NoPluralInfo,
kPreferencesObjectScriptElement,
kPreferencesSuiteScriptElement,
NoSampleScripts,
}
Property
{
kCstPrfPropertyScriptElement,
p_CustomPref,
"Custom preferences",
"The custom preference property...",
ObjectType( kCstPrfObjectScriptElement ),
{}
kNoAttributeClass,
}
Property
12
#10082 Making Your Plug-in Scriptable
Architectural Overview
{
kCstPrfPrefValuePropertyScriptElement,
p_PrefVal,
"pref value",
"The custom preference property value...",
BoolType,
{}
kNoAttributeClass,
}
Provider
{
kCstPrfScriptProviderBoss,
{
Parent { kApplicationObjectScriptElement },
Parent { kDocumentObjectScriptElement },
RepresentObject{ kCstPrfObjectScriptElement },
ParentProperty{ kCstPrfPropertyScriptElement,
kReadOnly },
Property{ kCstPrfPrefValuePropertyScriptElement,
kReadWrite },
}
}
}
};
•
Defining IDs
In the ScriptElementInfo resource above, we needed to define several IDs. The IDs can be
divided into the following categories:
1.
2.
IDs in the kScriptInfoIDSpace: This includes script architecture related IDs for suites,
objects, properties, and events. (These can be found in
{SDK}/source/samplecode/customprefs/CstPrfID.h.) These IDs fall into this
category:
—
kCstPrfObjectScriptElement
—
kCstPrfPropertyScriptElement
—
kCstPrfPrefValuePropertyScriptElement
IDs in the kClassIDSpace and kImplementationIDSpace: These are necessary since we
are creating bosses and providing implementations. (These can be found in
{SDK}/source/samplecode/customprefs/CstPrfID.h.) These IDs fall into this
category:
—
kCstPrfScriptProviderBoss
—
kCstPrfScriptProviderImpl
#10082 Making Your Plug-in Scriptable
13
#10082 Making Your Plug-in Scriptable
Architectural Overview
3.
4.
Script IDs: These symbols are macro definitions for 4-letter IDs (used by the integer
value they represent) that are used by the script manager to identify objects
(prefixed with a "c_"), properties ("p_"), events ("e_"), suites ("s_"), and
enumeration constants ("en_"). The assigned values must be unique. (These can be
found in {SDK}/source/samplecode/customprefs/CstPrfScriptingDefs.h.) These IDs
fall into this category:
—
#define c_CustomPref 'cprf'
—
#define p_CustomPref 'sprf'
—
#define p_PrefVal 'pVal'
GUIDs: These are Windows-specific IDs (GUID = Globally Unique IDs) that allow
COM to interact with the script manager. These were generated using the Microsoft
GUID generator GUIDGEN.EXE, then placed into a DECLARE_GUID macro
defined in {SDK}/source/public/includes/ScriptingWindowDefs.h. (The IDs below
can be found in {SDK}/source/samplecode/customprefs/CstPrfScriptingDefs.h.)
This ID falls into this category:
—
•
kCPrefs_CLSID
Creating a Script Provider Boss
Note that in the Provider part of the ScriptElementInfo resource above, we specified the
ID of our script provider boss as kCstPrfScriptProviderBoss. Now we need to define this
boss class. Our boss class inherits from kBaseScriptProviderBoss, as shown below. (This
can also be found in {SDK}/source/samplecode/customprefs/CstPrf.fr.)
Class
{
kCstPrfScriptProviderBoss,
kBaseScriptProviderBoss,
{
IID_ISCRIPTPROVIDER, kCstPrfScriptProviderImpl,
}
},
•
Writing an implementation for IScriptProvider
The last step in adding our Preferences Object is to provide an implementation of the
IScriptProvider interface. As shown in the Class declaration above, we shall call our
implementation class CstPrfScriptProvider. (This can be found in
{SDK}/source/samplecode/customprefs/CstPrfScriptProvider.cpp.) In this
implementation, we do the following:
14
—
Since our object is a Preference Object, the CstPrfScriptProvider inherits from the
PrefsScriptProvider base class, provided by the API (PrefsScriptProvider.h)
—
The constructor calls DefinePreference() to declare the new Custom Preferences
object to the PrefsScriptProvider base class. This base class will handle the ScriptID
for our the actual Custom Preferences object, so we don't need to worry about it.
#10082 Making Your Plug-in Scriptable
Recipes
—
•
We implement the AccessProperty() method that responds with the appropriate
action when the passed-in ScriptID is p_PrefVal. This ID corresponds to the bool16
value in our Preference Object.
Building and testing the scriptable plug-in
Once we have defined the ScriptElementInfo with IDs, created the script provider boss,
and written the IScriptProvider implementation, we are ready to build and test the plugin.
When you add a new script provider into the DOM, it is recommended that you clean out
your Saved Data files in the application defaults folder so that the necessary platformspecific resources will be generated at application startup. The platform-specific
resources are generated when the Saved Data files are regenerated. (Refer to Reviewing the
New Resources in the Recipes section for the path to your application defaults folder.)
You can then write a script to test the entire interaction between the script and your
scriptable plug-in.
Recipes
How to Add a New Object
If it is a UID-based object
1.
Create an IScript implementation for the object that subclasses class CScript and
aggregate to the object's boss.
2.
Aggregate the IScript implementation to the object's boss along with the default
IAutomationRefCount implementation kAutomationRefCountImpl (defined in
VisualBasicID.h, wrap with #ifdef WINDOWS . . . #endif) (in .fr file)
Class
{
kYourBoss,
kInvalidClass,
{
... //Other interfaces on your boss
IID_ISCRIPT, kYourScriptImpl,
#ifdef WINDOWS
IID_IAUTOMATIONREFCOUNT, kAutomationRefCountImpl,
#endif
}
}
3.
Create a script provider boss that subclasses kBaseScriptProviderBoss.
4.
Create an IScriptProvider implementation that subclasses class RepresentScriptProvider
and aggregate to the script provider boss.
#10082 Making Your Plug-in Scriptable
15
#10082 Making Your Plug-in Scriptable
Recipes
Class
{
kYourScriptProviderBoss,
kBaseScriptProviderBoss,
{
IID_ISCRIPTPROVIDER, kYourScriptProviderImpl,
}
}
5.
Create necessary ScriptElementInfo resources for the object, events, properties, and
provider defined above.
If it's NOT a UID-Based Object and if the object has a boss
1.
Make your boss a subclass of kBaseProxyScriptObjectBoss (if you can't do this, proceed as
"If the Object does NOT have a Boss" below).
2.
Create an IScript implementation for the object that subclasses class CProxyScript and
aggregate to the object's boss.
Class
{
kYourBoss,
kBaseProxyScriptObjectBoss,
{
... //Other interfaces on your boss
IID_ISCRIPT, kYourScriptImpl,
}
}
3.
Create a script provider boss that subclasses kBaseScriptProviderBoss.
4.
Create an IScriptProvider implementation that subclasses class RepresentScriptProvider
and aggregate to the script provider boss.
Class
{
kYourScriptProviderBoss,
kBaseScriptProviderBoss,
{
IID_ISCRIPTPROVIDER, kYourScriptProviderImpl,
}
}
5.
By default proxy objects are specified by index. If you want to specify by another property
(e.g., name):
a.
6.
16
In your implementation of IScript, override IScript::GetScriptObject and substitute
code to build your custom ScriptObject.
Create ScriptElementInfo resources for the object, events, properties, and provider.
#10082 Making Your Plug-in Scriptable
Recipes
If it's NOT a UID-Based Object and if the Object does NOT have a Boss
1.
Create a proxy script object boss that subclasses kBaseProxyScriptObjectBoss.
2.
Create an IScript implementation for the object that subclasses class CProxyScript and
aggregate to the proxy script object boss.
Class
{
kYourScriptObjectBoss,
kBaseProxyScriptObjectBoss,
{
IID_ISCRIPT, kYourScriptImpl,
}
}
3.
Create a script provider boss that subclasses kBaseScriptProviderBoss. Then create an
IScriptProvider implementation that subclasses class RepresentScriptProvider and
aggregate to the script provider boss
Class
{
kYourScriptProviderBoss,
kBaseScriptProviderBoss,
{
IID_ISCRIPTPROVIDER, kYourScriptProviderImpl,
}
}
4.
Create a script provider boss that subclasses kBaseScriptProviderBoss.
5.
Create an IScriptProvider implementation that subclasses class RepresentScriptProvider
and aggregate to the script provider boss.
Class
{
kYourScriptProviderBoss,
kBaseScriptProviderBoss,
{
IID_ISCRIPTPROVIDER, kYourScriptProviderImpl,
}
}
6.
In your implementation of IScriptProvider, call IScriptUtils::CreateProxyScriptObject()
to create one of your objects.
7.
By default proxy objects are specified by index. If you want to specify by another property
(e.g., name):
a.
Aggregate an appropriate data interface to your proxy script object's boss (e.g.
IID_ISTRINGDATA).
#10082 Making Your Plug-in Scriptable
17
#10082 Making Your Plug-in Scriptable
Recipes
8.
b.
In your implementation of IScriptProvider, set the specifier data manually after the
call to IScriptUtils::CreateProxyScriptObject().
c.
In your implementation of IScript, override IScript::GetScriptObject() and
substitute code to build your custom ScriptObject.
Create ScriptElementInfo resources for the object, events, properties, and provider.
If it is a Preference object
A preference object is an object that exists as a property on either the Application object or a
Document object. In the Object Model, this corresponds to preference data interfaces on
kWorkspaceBoss and kDocWorkspaceBoss, respectively.
1.
You generally don't need to define a boss or IScript implementation for a preferences
object, as it can use kBasePrefsScriptObjectBoss instead.
2.
Create a script provider boss that subclasses kBaseScriptProviderBoss. Then write an
IScriptProvider implementation that subclasses class PrefsScriptProvider and aggregate
to the script provider boss. In the constructor, call DefinePreference to link the object ID,
property ID, and prefs script object boss ID (generally kBasePrefsScriptObjectBoss)
Class
{
kYourPrefsScriptProviderBoss,
kBaseScriptProviderBoss,
{
IID_ISCRIPTPROVIDER, kYourPrefsScriptProviderImpl,
}
}
3.
Create necessary ScriptElementInfo resources for the objects, parent property, object
properties, and providers.
Exposing a New Property or Event on an Existing Object
This is similar to adding scripting support for a preference object.
1.
18
Create/modify a script provider.
•
Add support to an existing script provider, or
•
Make a new provider.
a.
Write an IScriptProvider implemenation that subclasses class CScriptProvider.
b.
Create a script provider boss that subclasses kBaseScriptProviderBoss and
aggregate the script provider implementation.
#10082 Making Your Plug-in Scriptable
Recipes
2.
Create ScriptElementInfo resources.
a.
For the new property or event
b.
For the new script provider (if adding a new script provider boss), or add to an
existing Provider resource.
Defining IDs
To keep the ID definitions easy to find in our plug-in project, you can define the various IDs
using the following organization.
•
Define your 4-letter ScriptIDs in (YourPlug-in)ScriptingDefs.h.
#define c_CustomPref
'cprf'
In the CustomPrefs sample plug-in, this is done in
{SDK}/source/sdksamples/customprefs/CstPrfScriptingDefs.h for convenience.
•
Define ScriptElementIDs in (YourPlug-in)ID.h file. See
{SDK}/source/sdksamples/customprefs/CstPrfID.h for a sample.
•
Define Windows CLSIDs in (YourPlug-in)ScriptingWindowDefs.h. See
{SDK}/source/sdksamples/customprefs/CstPrfScriptingDefs.h for a sample.
Reviewing the New Resources
InDesign CS/InCopy CS generates a new AppleScript dictionary (Mac only) or COM type
library (Win only) at run-time only when the plug-in list (or the any of the modification dates
on the plug-ins) changes, or when InDesign has to create a new InDesign Saved Data file. (One
way to force a new AppleScript dictionary or COM type library to be regenerated is to delete
the InDesign Saved Data file.) The result is written to the user's application defaults folder, in
the following locations:
•
Mac: ${home}/Library/Preferences/Adobe {InDesign or InCopy}/Version
3.0(J))/Scripting Support/. The AppleScript dictionary is stored as "Resources for
AppleScript.AETE".
•
Windows: %USERPROFILE%\Application Data\Adobe\{InDesign or InCopy)\Version
3.0(J)\Scripting Support\. The COM type library is stored as "Resources for Visual
Basic.tlb".
Reviewing the AppleScript Dictionary
Follow these steps to verify that your new script objects have been incorporated into the
AppleScript dictionary.
1.
Launch the AppleScript Editor on your system.
2.
File > Open Dictionary. . .
#10082 Making Your Plug-in Scriptable
19
#10082 Making Your Plug-in Scriptable
Recipes
3.
Select InDesign CS or InDesign CS_J, and browse the dictionary.
Reviewing the COM Type Library
Choose one of the following approaches to verify that your new script objects have been
incorporated into the COM type library.
Using Microsoft Visual C++
1.
Launch Visual C++.
2.
Select the Tools > OLE/COM Object Viewer menu.
3.
Select the File > View TypeLib... menu.
4.
Open "Resources for Visual Basic.tlb" from your application defaults folder and browse
the type library.
Using Microsoft Visual Basic
1.
Launch Visual Basic and create a new project.
2.
Select the Project > References… menu.
3.
From the Available References list, check "Adobe InDesign CS(J) Type Library" or "Adobe
InCopy CS Type Library" and click OK. If it doesn't show up in the list, click on the
Browse… button to find the "Resources for Visual Basic.tlb" in your application defaults
folder. Click OK on the References dialog.
4.
Select the View > Object Browser menu (or hit the F2 key). Pick "InDesign" or “InCopy”
from the first dropdown list to view the type library.
Using Microsoft Word or Excel's Macro Editor
20
1.
Launch Microsoft Word or Excel.
2.
Select the Tools > Macro > Visual Basic Editor menu.
3.
Select the Tools > References menu.
4.
From the Available References list, check "Adobe InDesign CS(J) Type Library" or "Adobe
InCopy CS Type Library" and click OK If it doesn't show up in the list, click Browse and
open "Adobe InDesign CS(J) Type Library" or "Adobe InCopy CS Type Library" from
your defaults folder.
#10082 Making Your Plug-in Scriptable
Recipes
5.
Select the View > Object Browser menu (or hit the F2 key). Pick "InDesign" or “InCopy”
from the first dropdown list to view the type library.
If you previously used a pre-release version of "InDesign 3.0" or "InCopy 3.0" (Windows only). If you started
developing your scriptable plug-in with a pre-release build of "InDesign 3.0" or "InCopy 3.0"
for Windows, you may need to edit your registry to make sure that references to the GUID for
the main COM Type Library doesn't point to "Adobe InDesign 3.0 Type Library" or "Adobe
InCopy 3.0 Type Library". Leaving these in may not result in a successful binding between your
Visual Basic script and the COM Type Library.
Follow these steps to remove such references:
1.
Make sure that you are not running any build of InDesign or InCopy, and that you are
not running your Visual Basic script that calls either application's COM Type Library.
2.
Run the Windows Registry Editor by typing "regedit" from the Start >> Run menu.
3.
If you were using InDesign 3.0, look for the registry key
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{6926C15F-E97E-4B13-8658A97D8C616945} and delete it. If you were using InCopy 3.0, look for the registry key
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{B8C5B92E-C373-401A-973F9DD03FEF0490} and delete it.
4.
Exit the Windows Registry Editor. This will save your changes automatically.
5.
Clean up the Saved Data files as described earlier. (NOTE: Replace the word "InDesign"
with "InCopy" for the InCopy saved data path.)
6.
Restart InDesign CS or InCopy CS. Doing so will regenerate the necessary registry keys.
7.
Restart your Visual Basic script environment, and remove references to the "Adobe
InDesign 3.0 Type Library" or "Adobe InCopy 3.0 Type Library" from your script or
project. In the Visual Basic IDE, go to the References dialog (Project >> References
menu) and uncheck the references, then click OK for the dialog.
8.
Then re-add references to the "Adobe InDesign CS Type Library" or "Adobe InCopy CS
Type Library" to your script or project.
9.
Save your script or project.
Supporting The InDesign Interchange File Format: Additional Info
For the most part, you are providing support for the InDesign Interchange file format simply
by providing a scripting provider for all of your custom objects. However, if you are
implementing a scripting provider on a custom page item object, there are a few extra
precautions you may want to consider.
1.
Make sure that your don’t rely on "with data" (parameters) during the Create event
(e_Create). This is because the application component that generates the InDesign
Interchange file format may not have all of the data to create the InDesign Interchange tag
attributes on your custom objects. Also, it may not set properties on your custom object
in any specific order. So in your script provider where you support the Create event
#10082 Making Your Plug-in Scriptable
21
#10082 Making Your Plug-in Scriptable
Recipes
(e_Create), you should be able to support instantiation without properties while using
reasonable defaults, and be able to tolerate setting properties in random order.
2.
You must remember to aggregate two additional interfaces: IXMLFragment
(IID_IXMLFRAGMENT) and IDOMElement (IID_IDOMELEMENT). Both interfaces
are located in the {SDK}/source/public/interfaces/xmedia folder, and the IIDs and
implementation IDs are defined in
{SDK}/source/public/interfaces/xmedia/XMLDocumentID.h. For example, kDocBoss
has a few AddIns:
AddIn
{
kDocBoss,
kInvalidClass,
{
IID_IXMLFRAGMENT,
kXMLDocumentContentFragmentImpl,
IID_IDOMELEMENT,
kDOMElementImpl // from API
}
Many other hierarchy objects, such as kFrameItemBoss and kMultiColumnItemBoss, just
aggregate the implementation kXMLNoContentFragmentImpl for the IXMLFragment
interface. (This implementation ID is also defined in
{SDK}/source/public/interfaces/xmedia/XMLDocumentID.h.)
AddIn
{
kFrameItemBoss,
kInvalidClass,
{
IID_IXMLFRAGMENT,
kXMLNoContentFragmentImpl,
IID_IDOMELEMENT,
kDOMElementImpl // from API
}
};
Also, if you have read-only properties that are set automatically by the application or by your
plug-in (e.g. file modification date/time), you may need a separate read/write version of the
property for use by the kXMLDocumentScriptManagerBoss so that the InDesign interchange
subsystem can update it while writing. For example, kPageCountPropertyScriptElement is a
read-only element that needs to be updated at the time of writing:
resource ScriptElementInfo(31)
{
kXMLDocumentScriptManagerBoss,
kInDesignAllLanguagesFS, k_Wild,
{
22
#10082 Making Your Plug-in Scriptable
Recipes
Provider
{
kSpreadScriptProviderBoss,
{
Object
{
kMasterSpreadObjectScriptElement
},
Property
{
kPageCountPropertyScriptElement,
kReadWrite
},
}
}
}
} ;
Refer to Client-Specific Script Element Resources for more information.
Verifying That Your New Script Objects Are Exported In The New InDesign Interchange
File Format
Follow these steps to make sure that your new script objects are exported in the new InDesign
Interchange file format (.inx).
1.
Load your scriptable plug-in into InDesign CS.
2.
Create a new document, and create necessary page items on the layout.
3.
Select the File > Export menu, and select the “InDesign Interchange” format. Save your
file with a .inx extension.
4.
Open the .inx file you just exported in a text editor or an XML viewer. Search for the 4
letter script IDs specified in your plug-in’s ScriptingDefs.h file.
For example, the CustomPrefs plug-ins prefval property (which is simply added onto an
existing object) will be saved in this file as such:
When the pref value is kFalse:
<cprf pVal="b_f" Self="rc_d.cprf[1]"/>
When the pref value is kTrue:
<cprf pVal="b_t" Self="rc_d.cprf[1]"/>
#10082 Making Your Plug-in Scriptable
23
#10082 Making Your Plug-in Scriptable
Recipes
Tips for Debugging the Scripting Architecture
You can use the following Debug-build only menu items to help in debugging your scriptable
plug-in.
•
Turn on Test > TRACE > ScriptInfoManager > Registration to trace the loading of script
info resources.
•
Turn on Test > TRACE > ScriptInfoManager > Resource Dependencies to enable asserts
when a dependent resource is missing.
•
Turn on Test > TRACE > ScriptRequestHandler to trace requests and see which script
providers are invoked.
•
Turn on Test > TRACE > CoreObjectSpecifier to trace the specification and resolution of
objects used by a JavaScript.
•
Turn on Test > TRACE > GenericCOMObject to trace operations on InDesign COM
objects used by a VisualBasic script.
•
From the Test > Scripting menu, you can dump the contents of the ScriptInfoManager
for various clients to a text file in the QA Logs folder. This makes it easy to diff changes
you are making or between builds.
Converting Existing Scriptable Plug-ins
To convert existing scriptable plug-ins made for previous versions of InDesign/InCopy, you
can follow these steps.
(NOTE: The following numbered list assumes you are able to use the TypeInfoConverter tool,
which is only available for Windows. You can find more information about converting
scriptable plug-ins in the section "Detailed information about porting a scriptable plug-in
from InDesign/InCopy 2.x to InDesign CS/InCopy".
1.
Find which plug-in you're working on, and make sure you have a compiled/built version
of that plug-in. (There is no need to load this in InDesign/InCopy at this point.) Make a
copy of the entire project for that plug-in, so you can make changes to the copy. Within
the project, find a script provider implementation (.cpp) and scripting resources (.r and
.odl). Open the source files in your IDE so you can view and edit them.
2.
Launch the the TypeInfoConverter tool
({SDK}/tools/typeinfoconverter/TypeInfoConverter.exe).
3.
Use the TypeInfoConverter tool to generate sections of ScriptElementInfo resources for
objects in the 2.x version of your scriptable plug-in. (The results of your conversions will
be placed in a Notepad text file, which you can save as a temporary .fr file. Further
documentation on how to use this tool is available in the "Appendix" section.)
4.
While looking at the AETE and ODL resources in your scriptable plug-in’s source files:
•
24
Change the name to be all lower case with spaces between words. (The script
manager will automatically capitalize the first letter in each word for Visual Basic.)
#10082 Making Your Plug-in Scriptable
Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS
•
Create one description. The format is that the first word starts with an uppercase.
Only add a period if another sentence is added and if so the next word should be
capitalized. All other words should be lower case.
•
Update the enumeration id, since the TypeInfoConverter tool can only provide a
guess.
•
Check if the data type is a unit (UnitType) since neither the ODL nor AETE contain
this information.
•
Make sure the data type is the same in the AETE and ODL.
5.
Using the TypeInfoConverter tool, create a Provider resource. Refer at the constructor in
your IScriptProvider implementation, and add the Object, Parent, Property, and Event to
the Provider resource.
6.
Save the file as [MyPlug-in]ScriptInfo.fr in your plug-in's folder and either add the
contents of the file to the plug-in's master .fr file, or simply #include it from the plug-in’s
master .fr file.
7.
Remove IID_IK2SERVICEPROVIDER from the script provider's boss resource, as it is no
longer needed.
8.
Add necessary element IDs to the plug-in's ID.h file.
9.
Change the code in the constructor of your IScriptProvider implementation.
•
Remove all the calls to DefineProperty(), DefineMethod, etc. However, do NOT
remove any DefinePreference() calls.
10.
If you find any differences between the way your AETE and ODL resources were defined,
keep in mind that that changes to the resources might break your end users’ scripts, so
you may want to document these changes somewhere.
11.
Be sure to compile and load your scriptable plug-in on both platforms using the debug
build of InDesign/InCopy. This would allow you to check for asserts during loading of the
new resources and creation of the AppleScript dictionary and COM Type Library.
Detailed Information About Porting A Scriptable Plug-in From
InDesign 2.x To InDesign CS
Converting Script Providers
There are two interconnected parts to creating a ScriptElementInfo resource. The first involves
extracting information on individual script elements (objects, properties, events, etc.) from the
existing AETE resource and ODL resource (compiled into a type library). The second involves
extracting information on how all these elements relate to each other from a script provider.
Generally it is easiest to work backwards, beginning with a script provider. In the old
paradigm, the constructor for a script provider contained all the information about which
#10082 Making Your Plug-in Scriptable
25
#10082 Making Your Plug-in Scriptable
Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS
elements (objects, properties, events, etc.) that script provider supports. In the new paradigm,
this information is stored in a script Provider resource.
There are three kinds of script providers:
1.
Those that inherit from RepresentScriptProvider represent one or more objects, and
usually support related events and properties.
2.
Those that inherit from CScriptProvider add in support for one or more events and/or
properties to one or more existing objects.
3.
Those that inherit from PrefsScriptProvider represent a preferences object, and often
support one or more related properties.
Converting A Represent Script Provider
Represent script providers are recognizable by the fact that their implementation typically
inherits from the RepresentScriptProvider base class. The constructor will typically include one
or more DefineParent statements, one or more DefineObject and DefineCollection pairs of
statements, and a variety of DefineEvent and DefineProperty statements.
1.
Remove all of the code within the constructor of the script provider. Take note of which
objects appear within the DefineObject statements.
2.
Use the TypeInfoConverter tool to generate a ScriptElementInfo resource for the
object(s), and any events and properties that appear in the script provider's constructor.
You do not need to generate resources for common events and properties that already
have resources defined elsewhere (e.g. the Count and Delete events, the Name property,
etc.).
3.
Note that Object resources include a reference to a suite script element, which is used by
AppleScript to group related objects and events together. Since suites are not used by VB,
Suite resources are not automatically generated by the TypeInfoConverter tool. You can
copy the information you need to create one by hand from the relevant suite definition in
the existing AETE resource file.
The resulting resource should look something like this:
resource ScriptElementInfo(1)
{
kCoreScriptManagerBoss,
kWildFS, k_Wild,
{
Object
{
kDocumentObjectScriptElement,
c_Document,
"document",
"A document",
kDocument_CLSID,
c_Documents,
"documents",
26
#10082 Making Your Plug-in Scriptable
Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS
"All open documents",
kDocuments_CLSID,
kNonIDBasedObjectScriptElement,
kBasicSuiteScriptElement,
}
Event
{
kCreateDocumentEventScriptElement,
e_Create,
"add",
"Create a new document",
ObjectType( kDocumentObjectScriptElement ),
"The new document",
{
p_ShowInWindow,
"show in window",
"Whether to open the document in a window",
BoolType,
kOptional,
}
}
Enum
{
kSaveOptionsEnumScriptElement,
en_SaveOptions,
"save options",
"Options for saving before closing a document",
{
en_No,
"no",
"Don't save changes",
en_AskUserSaveFile,
"ask",
"Ask user whether to save changes",
en_Yes,
"yes",
"Save changes",
}
}
Event
{
kCloseDocumentEventScriptElement,
e_Close,
"close",
"Close the document",
VoidType,
{
#10082 Making Your Plug-in Scriptable
27
#10082 Making Your Plug-in Scriptable
Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS
p_SaveOptions,
"save changes",
"Whether to save changes ...",
EnumType( kSaveOptionsEnumScriptElement ),
kOptional,
keyAEFile,
"saving in",
"The file in which to save the document",
FileType,
kOptional,
}
}
... // Other events
Property
{
kModifiedPropertyScriptElement,
p_Modified,
"modified",
"Whether the object has been modified",
BoolType,
{}
}
... // Other properties
Provider
{
kDocumentScriptProviderBoss,
{
Parent{ kApplicationObjectScriptElement },
RepresentObject{ kDocumentObjectScriptElement },
//kCountEventScriptElement is a common event
// defined elsewhere
CollectionEvent{kCountEventScriptElement},
CollectionEvent{kCreateDocumentEventScriptElement},
Event{ kCloseDocumentEventScriptElement },
... // other events
//kNamePropertyScriptElement is a common property
// defined elsewhere
Property{ kNamePropertyScriptElement,
kReadOnly },
Property{ kModifiedPropertyScriptElement,
kReadOnly },
... // other properties
}
}
}
} ;
28
#10082 Making Your Plug-in Scriptable
Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS
Converting An Add-in Script Provider
Add-in script providers are recognizable by the fact that their implementation typically inherits
from the CScriptProvider base class. The constructor will typically include one or more
DefineObject statements, and a variety of DefineEvent and/or DefineProperty statements.
1.
Remove all of the code within the constructor of the script provider. Take note of which
objects appear within the DefineObject statements.
2.
Use the TypeInfoConverter tool to generate a ScriptElementInfo resource for any events
and properties that appear in the script provider's constructor.
Converting A Preferences Script Provider
Preferences script providers are recognizable by the fact that their implementation typically
inherits from the PrefsScriptProvider base class. The constructor will typically include one or
more DefineParent statements, a DefinePreference statement, and sometimes some
DefineProperty statements. Preferences objects are a bit unusual in that they are added as a
singleton child to their parent object (via the ParentProperty mechanism). Thus a preferences
script provider's constructor (in the old way) and Provider resource (in the new way) contains
information about the preferences object itself, the properties of the preferences object, the
parent(s) of the preferences object, and the property on the parent object(s) that allows access
to the preferences object.
1.
Do not remove the DefinePreference statement(s), but do remove any other code
(generally DefineProperty statements) within the constructor of the script provider. Take
note of which object appears within the DefinePreference statement - - also note one or
more of the parent objects.
2.
Use the TypeInfoConverter tool to generate an ScriptElementInfo resource for the
preference object, and any properties that appear in the script provider's constructor.
3.
You will also need to generate a property resource to assign to the parent objects that have
access to this preferences object. The easiest way to do this in the TypeInfoConverter tool
is to look in the properties list of one of the parent objects (e.g., find the
"DocumentPreferences" property on the "Document" object). Double-click on this to
generate a preferences property resource for your preferences object. Reference this
property in the Provider resource for your preferences object, but using the
ParentProperty label.
4.
Recommended style is for the name of preferences objects to have the word "preference"
(singular), whereas the preferences property has the word "preferences" (plural).
The resulting resource should look something like this:
resource ScriptElementInfo(1)
{
kCoreScriptManagerBoss,
kWildFS, k_Wild,
{
Object
{
#10082 Making Your Plug-in Scriptable
29
#10082 Making Your Plug-in Scriptable
Detailed Information About Porting A Scriptable Plug-in From InDesign 2.x To InDesign CS
kDocPrefsObjectScriptElement,
c_DocPref,
//"preference" appears here as singular
"document preference",
"Document preferences",
kDocPref_CLSID,
//Preferences objects don't have plural forms
NoPluralInfo,
//Preferences objects are always based on this type
kPreferencesObjectScriptElement,
//Most preferences objects are in this suite
kPreferencesSuiteScriptElement,
}
Property
{
kDocPrefsPropertyScriptElement,
p_DocPref,
//"preferences" appears here as plural
"document preferences",
"Document preferences",
ObjectType( kDocPrefsObjectScriptElement ),
{}
}
Property
{
kNumPagesPropertyScriptElement,
p_DPNumPages,
"pages per document",
"The number of pages in a document",
Int32Type,
{}
}
... // other Property resources
Provider
{
kDocPrefsScriptProviderBoss,
{
Parent{ kApplicationObjectScriptElement },
Parent{ kDocumentObjectScriptElement },
RepresentObject{ kDocPrefsObjectScriptElement },
//This will be added to the parent(s)
ParentProperty{ kDocPrefsPropertyScriptElement },
//This will be added to the preferences object
Property{ kNumPagesPropertyScriptElement,
kReadWrite },
...// other properties
}
30
#10082 Making Your Plug-in Scriptable
References
}
}
} ;
API Changes Between InDesign 2.0.x And InDesign CS
For details on API changes, including changes to IScriptProvider, refer to the API Advisor
report between InDesign/InCopy 2.0.1 and InDesign CS. This HTML file is located in
{SDK}/docs/references/APIAdvisorID2_vs_ID3.html.
References
Resource Definitions
ScriptElementInfo Resource
Defined in: {SDK}/source/public/includes/ScriptInfoTypes.fh
ScriptElementInfo is the name for a core scripting resource. Note that there may be more than
one ScriptElementInfo resource in a ScriptInfo .fr file.
Like every resource, the first thing included in a ScriptElementInfo resource is the resource's id,
which must be unique to the plug-in.
Next is the client specifier (the ClassID of a script manager boss). Typically this is
kCoreScriptManagerBoss, which indicates that the resources are applicable to all clients. If you
want to make a script element visible to only one client, you can put the boss class ID of the
client's script manager (e.g. kMyScriptManagerBoss) instead. Refer to Client-Specific Script
Element Resources for more details on this field.
The next thing in a ScriptElementInfo resource is a feature set ID and a locale ID. In most
cases, these will be kWildFS, k_Wild, which means this resource applies to all feature sets and
all UI locales.
The most absolutely basic (and also totally useless) ScriptElementInfo resource would look
something like:
resource ScriptElementInfo(1)
{
kCoreScriptManagerBoss,
kWildFS, k_Wild,
{
}
} ;
To make it useful, include one or more Suite, Object, Event, Property, Enum, and Provider
resources inside.
#10082 Making Your Plug-in Scriptable
31
#10082 Making Your Plug-in Scriptable
References
Strings In Resources
The name of an element should be all lowercase (e.g., "delete", "text frame"), unless you want to
force uppercase for an acronym (e.g., "URL") or product name (e.g., "InDesign basics"). The
name of an element is never translated.
The description of an element should be sentence case (e.g., initial word capitalized) with no
closing punctuation, unless the description is more than one sentence long, in which case all
sentences except the last should have closing punctuation.
Sample scripts are not translated, but we use a string key to look up the full script in the nontranslate string table.
Object-Sensitive Descriptions
It often makes sense for the description field(s) of an event or property element to contain the
name of the relevant object. For example:
•
Description of the name property:
"The name of the swatch"
•
Description of the count event:
"The number of swatches"
•
Description of the return value for the create event:
"The new swatch"
By using ^Object for the singular form and ^Objects for the plural form, the name of the
relevant object will be substituted at runtime. For example:
•
Description of the name property:
"The name of the ^Object"
•
Description of the count event:
"The number of ^Objects"
•
Description of the return value for the create event:
"The new ^Object"
This is particularly useful for events and properties that appear on multiple objects.
Suite Resources
Object resources include a reference to a suite script element, which is used by AppleScript to
group related objects and events together. A typical Suite resource looks like:
Suite
{
//The suite's ScriptElementID
32
#10082 Making Your Plug-in Scriptable
References
// (defined in the plug-in's ID.h file)
kBasicSuiteScriptElement,
//The suite's ScriptID (defined in
// {SDK}/source/public/includes/ScriptingDefs.h)
s_InDesignBasicSuite,
//The name of the suite
"InDesign basics",
//A description of the suite
"Terms applicable to many InDesign operations",
}
ScriptElementIDs for common suites are defined in
{SDK}/source/public/interfaces/Architecture/ScriptingID.h.
Object Resources
A typical Object resource looks like:
Object
{
//The object's ScriptElementID
// (defined in the plug-in's ID.h file)
kSpreadObjectScriptElement,
//The object's ScriptID (defined in
// {SDK}/source/public/Includes/ScriptingDefs.h)
c_Spread,
//The name of the object
"spread",
//A description of the object
"A spread",
//The Windows CLSID (see below) of the object
kSpread_CLSID,
//The plural form of the object's ScriptID
c_Spreads,
//The name of the plural form
"spreads",
//A description of the plural form
"Every spread",
//The Windows CLSID of the plural form
kSpreads_CLSID,
//The based-on type of the object
kUniqueIDBasedObjectScriptElement,
//The suite to which the object belongs
kLayoutSuiteScriptElement,
//Indicates there are no sample scripts
NoSampleScripts,
}
#10082 Making Your Plug-in Scriptable
33
#10082 Making Your Plug-in Scriptable
References
Some objects are singletons and don't have a plural form. The Application object is one
example, as are preferences objects. These look like:
Object
{
kMarginPrefsObjectScriptElement,
c_MarginPref,
"margin preference",
"Margin preferences",
kMarginPref_CLSID,
//No plural form
NoPluralInfo,
//The based-on type of a preferences object
kPreferencesObjectScriptElement,
kPreferencesSuiteScriptElement,
NoSampleScripts,
}
A Note On Scripting Object Inheritance
Objects are generally non-ID-based (i.e., proxy objects in scripting; e.g., Text objects,
TabStops), non-unique-ID-based (i.e., those that have an id, but not one that is unique across
the document; e.g., Cells, XMLElements), or unique-ID-based (i.e., all UID-based objects).
This is defined in the object's core scripting resource by using the appropriate id:
kNonIDBasedObjectScriptElement, kNonUniqueIDBasedObjectScriptElement, or
kUniqueIDBasedObjectScriptElement respectively.
One way to tell whether an object is ID-based or non-ID-based is to look at the IScript
implementation for the object. If it inherits from CProxyScript, then it is probably non-IDbased (or possibly non-unique-ID-based). If it inherits from CScript, then it is probably IDbased.
An object may be based on another by specifying the base object in the object's core scripting
resource. For example, Oval objects are based on PageItem objects:
Object
{
kPageItemObjectScriptElement,
c_PageItem,
"page item",
"A generic page item. Any page item can be ...",
kPageItem_CLSID,
c_PageItems,
"page items",
"All page items",
kPageItems_CLSID,
kUniqueIDBasedObjectScriptElement,
kLayoutSuiteScriptElement,
}
34
#10082 Making Your Plug-in Scriptable
References
Object
{
kOvalObjectScriptElement,
c_Oval,
"oval",
"An oval",
kOval_CLSID,
c_Ovals,
"ovals",
"All ovals",
kOvals_CLSID,
kPageItemObjectScriptElement, /* inheritance */
kLayoutSuiteScriptElement,
}
If an object is based on another, it inherits the base object's events and properties (but not its
parents, children/collections, or collection events).
It also implies that a collection of the base objects (e.g., a spread's PageItems collection) will
automatically include any existing subclass objects (e.g., the spread's Ovals). This is a
requirement for the InDesign Interchange file format and is programatically dependent upon
the implementation of the IScriptProvider::GetObject methods for the base object's collection.
This approach to object inheritance replaces the concept of a generic type that was previously
implemented via IScript::GetGenericType in InDesign 2.x and earlier.
Windows CLSIDs
The CLSID must be defined using the new
DECLARE_GUID
macro (which is defined in {SDK}/public/Includes/ScriptingWindowDefs.h) to be usable in a
scripting resource. For example:
#define kDocument_CLSID { 0x1a5e8db4, 0x3443, 0x11d1, {
0x80, 0x3c, 0x0, 0x60, 0xb0, 0x3c, 0x2, 0xe4 } }
DECLARE_GUID( Document_CLSID, kDocument_CLSID ) ;
Event Resources
Typical Event resources look like these:
Event
{
//The event's ScriptElementID
// (defined in the plug-in's ID.h file)
kDeleteEventScriptElement,
//The event's ScriptID (defined in
// {SDK}/source/public/Includes/ScriptingDefs.h)
e_Delete,
//The name of the event
#10082 Making Your Plug-in Scriptable
35
#10082 Making Your Plug-in Scriptable
References
"delete",
//A description of the event
"Delete the ^Object",
//The return type--this event has no return value
VoidType,
//Parameters--this event has none
{}
}
Event
{
kOpenEventScriptElement,
e_Open,
"open",
"Open a document",
//This event returns a Document object
VariableType
{
ObjectType(kDocumentObjectScriptElement),
ObjectType(kBookObjectScriptElement),
ObjectType(kLibraryObjectScriptElement)
},
//A description of the return value
"The opened document, book, or library",
{
//Parameters
// (1) ScriptID,
// (2) name,
// (3) description,
// (4) type,
// (5) optional/required
// (6) default value (if optional)
keyDirectObject,
"from",
"One or more file paths",
VariableType
{
FileType,
FileArrayType( kVariableLength )
},
kRequired,
p_ShowInWindow,
"showing window",
"Whether to show the document in a window",
BoolType,
kOptional,
BoolDefault(kTrue),
36
#10082 Making Your Plug-in Scriptable
References
}
}
Parameter Defaults
You must specify a default value for all optional parameters using one of the following
declarations:
•
NoDefault
•
BoolDefault( true or false )
•
Int16Default( short integer )
•
Int32Default( long integer )
•
StringDefault( string )
•
RealDefault( real number )
•
EnumDefault( enumerator )
You will notice that there are no declarations for unit, data, file, object, record, or variable or
array types. For some of these types, you may use one of the permitted default value types. For
example, a Unit can typically be specified as a real or a string; a variable type that includes
object and string types, could be specified using a string. However, there's currently no way to
specify an array of default values for an array type. Parameters that don't have a default value or
for which none can be indicated using the existing specifiers, must be declared as NoDefault.
Some Special Events
•
•
Create/Add/Make:
1.
Are known collectively as the "Create" event but is called "Add" in Visual Basic and
"make" in AppleScript; use e_Create as the ScriptID and "add" as the name in the
Event resource.
2.
Most objects can use the default kCreateEventScriptElement, unless they have
required parameters for the create.
3.
Is always a CollectionEvent in a Provider resource. e.g.: CollectionEvent{
kCreateEventScriptElement },
Count:
1.
Is considered a property in VB and appears on collections (the plural form of an
object, e.g. "Books") in the type library.
2.
Is always a CollectionEvent in a Provider resource: e.g.: CollectionEvent{
kCountEventScriptElement },
Property Resources
Typical Property resources look like these:
Property
{
#10082 Making Your Plug-in Scriptable
37
#10082 Making Your Plug-in Scriptable
References
//The property's ScriptElementID
// (defined in the plug-in's ID.h file)
kBoundsPropertyScriptElement,
//The property's ScriptID (defined in
// {SDK}/source/public/Includes/ScriptingDefs.h)
p_Bounds,
//The name of the property
"bounds",
//A description of the property
"The bounds of the window",
//Types that the property could return on a "Get"
UnitArrayType(4),
//Any additional types that the property will accept
// on a "Set"
{}
//The boss ID of the equivalent page item or
// text attribute boss
kNoAttributeClass,
}
Property
{
kLogFilePropertyScriptElement,
p_LogFile,
"log file",
"The file to use for a log",
//The property's inherent type is "File"
FileType,
{
//You could also set the property by using a "String"
StringType
}
kNoAttributeClass,
}
//This will be referenced as a ParentProperty
// in the Provider resource but
Property
{
// is still defined by a Property resource
kMarginPrefsPropertyScriptElement,
p_MarginPref,
"margin preferences",
"Margin preferences",
//This property points to another object through
// that object's ScriptElementID
ObjectType( kMarginPrefsObjectScriptElement ),
{}
38
#10082 Making Your Plug-in Scriptable
References
kNoAttributeClass,
}
Some Special Properties
•
•
Name property
—
Is predefined in {SDK}/source/components/script/resources/ScriptingScriptInfo.fr
as kNamePropertyScriptElement.
—
Does need to be added into a Provider resource that supports the Name property.
e.g.: Property{ kNamePropertyScriptElement, kReadWrite },
Parent property
—
•
ID property
—
•
Is automatically generated and so does not need to be added into any Provider
resource.
Is automatically generated and so does not need to be added into any Provider
resource.
Class property
—
Is automatically generated and so does not need to be added into any Provider
resource.
Enum Resources
Enum resources look like this:
Enum
{
//The enum's ScriptElementID
// (defined in the plug-in's ID.h file)
kSaveOptionsEnumScriptElement,
//The enum's ScriptID (defined in
// {SDK}/source/public/Includes/ScriptingDefs.h)
en_SaveOptions,
//The name of the enum
"save options",
//A description of the enum
"Options for saving before closing a document",
//A list of enumerators for the enum
{
// An enumerator's ScriptID, name, and description
en_No,
"no",
"Don't save changes",
en_AskUserSaveFile,
#10082 Making Your Plug-in Scriptable
39
#10082 Making Your Plug-in Scriptable
References
"ask",
"Ask user whether to save changes",
en_Yes,
"yes",
"Save changes",
}
}
Provider Resources
The Provider resource ties together the other resources:
•
It indicates which events and properties are available on which objects.
•
It defines the object hierarchy.
•
And it specifies which script provider boss knows how to handle the referenced object(s),
event(s) and property(s).
A Provider resource looks like this:
Provider
{
//The class id of the script provider boss that handles
// the referenced object(s), event(s) and property(s)
kDocumentScriptProviderBoss,
{
//The parent object of the referenced object(s)
// in the object hierarchy
Parent{ kApplicationObjectScriptElement },
//The object which has the referenced parent(s),
// event(s) and property(s)
RepresentObject{ kDocumentObjectScriptElement },
//An event on a collection of the referenced object(s)
//(typically Count/Create events are collection events)
CollectionEvent{ kCountEventScriptElement },
//An event on the referenced object(s)
Event{ kCloseDocumentEventScriptElement },
//A property on the referenced object(s), and whether
// the property is read-write/read-only
Property{ kNamePropertyScriptElement, kReadOnly },
...
//Other references to parents, objects, events,
// and/or properties handled by this script provider
}
}
Types
Types are found in Property and Event resources.
40
#10082 Making Your Plug-in Scriptable
References
TABLE 1.2 Basic Types
Description
ODFRC Type
ODL Type
AETE Type
Void
VoidType
void
typeNull
Short Integer
Int16Type
short
typeShortInteger
Long Integer
Int32Type
long
typeLongInteger
Boolean
BoolType
VARIANT_BOOL
typeBoolean
String
StringType
BSTR
typeChar
Measurement unit
UnitType
double
cFixed
Real number
RealType
double
cFixed
Date or Time
DateType
DATE
typeLongDateTime
File name or path
FileType
BSTR or VARIANT
typeAlias
Enum
EnumType (
enum_id )
enum's name
enum's ScriptID
Object
ObjectType (
object_id )
IDispatch*
cObjectSpecifier
Array of short
integers
Int16ArrayType (
length )
VARIANT
typeShortInteger
(listOfItems)
Array of long
integers
Int32ArrayType (
length )
VARIANT
typeLongInteger
(listOfItems)
Array of booleans
BoolArrayType (
length )
VARIANT
typeBoolean
(listOfItems)
Array of strings
StringArrayType (
length )
VARIANT
typeChar
(listOfItems)
Array of
measurement units
UnitArrayType (
length )
VARIANT
cFixed
(listOfItems)
Array of real
numbers
RealArrayType (
length )
VARIANT
cFixed
(listOfItems)
Array of
dates/times
DateArrayType (
length )
VARIANT
typeLongDateTime
(listOfItems)
Array of file
names/paths
FileArrayType (
length )
VARIANT
typeAlias
(listOfItems)
Array of enum
values
EnumArrayType (
enum_id , length )
VARIANT
enum's ScriptID
(listOfItems)
Array of objects
ObjectArrayType (
object_id , length )
IDispatch*
cObjectSpecifier
(listOfItems)
#10082 Making Your Plug-in Scriptable
41
#10082 Making Your Plug-in Scriptable
References
Description
ODFRC Type
ODL Type
AETE Type
List of key/value
pairs
RecordType
VARIANT
typeAERecord
TABLE 1.3 Special Types
Description
ODFRC Type
ODL Type
AETE Type
Variable
VariableType {
type_list }
VARIANT
typeWildCard
Array of variable
contents
VariableArrayType
( length ) {
type_list }
VARIANT
typeWildCard
(listOfItems)
Key:
•
length: the number of items in the array; if it varies, use kVariableLength.
•
enum_id: a valid ScriptElementID of an enum.
•
object_id: a valid ScriptElementID of an object.
•
type_list: a list of one or more basic types separated by commas.
Types are actually defined as ODFRC macros (in
{SDK}/source/public/Includes/ScriptInfoTypes.fh).
Some Type Examples
TABLE 1.4 Special Types
42
VariableType{ FileType, FileArrayType(
kVariableLength ) }
A single file or an array of files
VariableType{ ObjectType(
kAnyObjectScriptElement ), ObjectArrayType(
kAnyObjectScriptElement, kVariableLength ) }
A single object of any kind, or an array of any kind
of objects
VariableType{ StringType, EnumType(
kNothingEnumScriptElement ) }
A string or the enumeration 'none'
VariableType{ EnumType(
kAnchorPointEnumScriptElement ),
UnitArrayType(2) }
An anchor point enumeration or an array of two
unit values (i.e., a measurement point)
VariableType{ ObjectType(
kSwatchObjectScriptElement ), StringType }
A swatch object or a string (i.e., the name of the
swatch)
VariableType{ EnumType(
kUIColorsEnumScriptElement ),
RealArrayType(3) }
One of the enumerated UI colors or an array of
three reals (i.e., a list of RGB values)
#10082 Making Your Plug-in Scriptable
References
VariableArrayType( kVariableLength ) {
ObjectType( kPageItemObjectScriptElement
),ObjectType( kImageObjectScriptElement ),
ObjectType( kEPSObjectScriptElement ),
ObjectType( kPDFObjectScriptElement ) }
An array of one or more page items, images, EPSes,
and/or PDFs
Object-Sensitive Types
It occasionally makes sense for the type of a property or in an event to be the type of the
relevant object. For example:
•
Type of the AppleScript object reference property
•
Type of the object returned by a create or duplicate event
•
Type of a parameter to the move event
For these cases, use ObjectType( kContainerObjectScriptElement ) as the type. This is
particularly useful for events and properties that appear on multiple objects.
Even more rarely a property or event parameter may have the type of the relevant object's
parents. For example:
•
Type of the Parent property
•
Type of the reference parameter to a move, duplicate, or create event
For these cases, use ObjectType( kContainerParentScriptElement ) as the type.
These options are particularly useful for events and properties that appear on multiple objects.
Record Type
A "record" is a list of key/value pairs, where the key is the ScriptID of a property and the value is
any appropriate one for that property. A RecordType declaration takes an object_id, which is
the ScriptElementID of the object to which the properties in the record apply. For example:
•
RecordType( kDocumentEventScriptElement ) indicates that the keys in the record
correspond to properties of the Document object.
•
Furthermore, you can use an object-sensitive type such as RecordType(
kContainerObjectScriptElement ): in a property resource this would indicate that the
keys correspond to properties on the object that contains the property; in an event's
parameter resource this would indicate that they correspond to properties on the object
that contains the event.
Defining Script Element IDs
You can use the following text as a template by copying it into your plug-in's ID.h file.
Recommended style is to group the ids by element type (objects, events, properties, etc.).
//Script Element IDs
//Suites
#10082 Making Your Plug-in Scriptable
43
#10082 Making Your Plug-in Scriptable
References
DECLARE_PMID(kScriptInfoIDSpace, k???SuiteScriptElement, k???Prefix + 1)
//Objects
DECLARE_PMID(kScriptInfoIDSpace, k???ObjectScriptElement, k???Prefix + 40)
//Events
DECLARE_PMID(kScriptInfoIDSpace, k???EventScriptElement, k???Prefix + 80)
//Properties
DECLARE_PMID(kScriptInfoIDSpace, k???PropertyScriptElement,k???Prefix + 140)
//Enums
DECLARE_PMID(kScriptInfoIDSpace, k???EnumScriptElement, k???Prefix + 220)
Overloading an Existing Event or Property
Two objects may have the same property or event, but with a slightly different definition. For
example, the description may be different, or the type of the property, or the parameters of the
event. In this case, you will need to define two different resources.
•
Define a unique ScriptElementID for each resource
•
The name and ScriptID must be identical in both resources
•
Other information (description, type, parameters, etc.) may be identical or different as
desired
Removing a Core Scripting Resource
To remove a scripting resource, simply delete it. You will also need to delete any other
references to its Script Element ID (in any Provider resources and in the plug-in's ID.h file).
Client-Specific Script Element Resources
Certain script elements are only applicable to one client of the scripting DOM. These script
elements are declared the same way as core resources, but using the ClassID of the client's
script manager boss.
These script elements are managed automatically by the same IScriptManagerInfo interface,
and are visible or not depending on the client that has requested the info.
Elements That Are Only Visible to One Client. All ScriptElementInfo resources specify a client--the
ClassID of a script manager boss. For core resources, this is kCoreScriptManagerBoss. For
client-specific script elements, this is the ClassID of the client's script manager boss. For
example:
resource ScriptElementInfo(10)
{
//ClassID of the script manager for the client for which
// these resources are exposed
kMyScriptManagerBoss,
kWildFS, k_Wild,
{
44
#10082 Making Your Plug-in Scriptable
References
//Client-specific Object, Event, Property, and
// Provider resources
...
}
} ;
On first launch, all script elements are loaded into a single "database", which is contained in the
implementation of IScriptInfoManagerFactory and stored in SavedData. Each element
includes a member datum of the ElementContext class (defined in ScriptInfo.h), which
specifies the element's client (core or specific), feature set, locale, and version (first and last).
When a client wants access to the script info database, it must specify a RequestContext
(defined in ScriptInfo.h), which indicates what client, feature set, locale, and version the client
is interested in. The client calls IScriptUtils::QueryScriptInfoManager or
IScriptUtils::QueryScriptRequestHandler. If this is the first request using a particular
RequestContext, the ScriptInfoManagerFactory will create a new ScriptInfoManager boss
containing only the relevant elements. If this RequestContext has previously been received, the
ScriptInfoManagerFactory will return the appropriate interface on the existing boss.
The elements contained in a ScriptInfoManager boss for a particular RequestContext will
include ALL core script elements PLUS all the elements that are specific to the specified client
(if any).
Elements That Are Defined Differently By Different Clients. Attributes of a script element that are intrinsic
to its definition (i.e., that appear in fields of the script element's resource), can be changed on a
per-client basis. For example, a property might have a different type or an event might have
different parameters.
One way to do this is to define a new script element with its own ScriptElementID, name, and
ScriptID. In this case, both versions of the element would be available. Alternatively, you can
use the same ScriptElementID, but change some or all of the other attributes. In this case, only
the client-specific version of the element would be available for that client. In either case, the
alternative script element could be handled by the same or a different script provider as the
original one, since that is determined by the Provider resource.
For example, here the Delete event for the Swatch object has one parameter by default, but no
parameters for my client (which also uses a different script provider to handle the event):
resource ScriptElementInfo(20)
{
kCoreScriptManagerBoss,
kWildFS, k_Wild,
{
Event
{
kDeleteSwatchEventScriptElement,
e_Delete,
"delete",
"Delete swatch",
VoidType,
#10082 Making Your Plug-in Scriptable
45
#10082 Making Your Plug-in Scriptable
References
{
p_Replace,
"replacing with",
"The swatch to apply in place of this one",
VariableType
{
ObjectType(kSwatchObjectScriptElement),
StringType
},
kRequired,
}
}
Provider
{
kSwatchScriptProviderBoss,
{
Object
{
kSwatchObjectScriptElement
},
Property
{
kDeleteSwatchEventScriptElement
},
}
}
}
} ;
resource ScriptElementInfo(21)
{
kMyScriptManagerBoss,
kWildFS, k_Wild,
{
Event
{
kDeleteSwatchEventScriptElement,
e_Delete,
"delete",
"Delete swatch",
VoidType,
{
}
}
Provider
{
kMySwatchScriptProviderBoss,
{
46
#10082 Making Your Plug-in Scriptable
References
Object
{
kSwatchObjectScriptElement
},
Event
{
kDeleteSwatchEventScriptElement
},
}
}
}
} ;
Relationships Between Elements That Are Different For Different Clients. In some cases, attributes of a script
element that are related to its behavior for a specific object are set in the Provider resources. For
example, whether an object has a particular parent or whether a property is read-only or readwrite. For a particular client to obtain behavior that is different from the core attributes set in
the core resources, define a separate Provider resource in a context-sensitive ScriptElementInfo
resource for that client.
For example, here the Name property of the Hyphenation Exception object is read-only by
default, but read-write for my client:
resource ScriptElementInfo(30)
{
kCoreScriptManagerBoss,
kWildFS, k_Wild,
{
Provider
{
kHyphExceptionScriptProviderBoss,
{
Object
{
kHyphenationExceptionObjectScriptElement
},
Property
{
kNamePropertyScriptElement,
kReadOnly
},
}
}
}
} ;
resource ScriptElementInfo(31)
{
kMyScriptManagerBoss,
#10082 Making Your Plug-in Scriptable
47
#10082 Making Your Plug-in Scriptable
References
kWildFS, k_Wild,
{
Provider
{
kHyphExceptionScriptProviderBoss,
{
Object
{
kHyphenationExceptionObjectScriptElement
},
Property
{
kNamePropertyScriptElement,
kReadWrite
},
}
}
}
} ;
Relationships Between Elements That Are Not Applicable To A Particular Client. By default, elements that are
specified in a core resource will be available in all clients, although sometimes these elements
will have an alternative, client-specific definition (see above). However, in most cases, whether
or not these elements are actually accessible to a user of the client is determined by whether
they appear in a Provider resource. That is because it's the Provider resources that determine
the parent/child relationships of the DOM hierarchy, as well as which events and properties are
supported by which objects. This makes it possible for a client to hide a particular element by
hiding relationships between elements. (Note: This isn't true of core Suite or Enum elements,
which therefore can not be hidden.)
To remove a relationship, define a Provider resource that uses kNotSupported as the id of the
script provider. Doing so means that any relationships in that Provider resource will
specifically NOT be supported for the client in question.
In the following example, a client-specific Provider resource is used to add additional parents
for the Link object and to remove a property that these parents use to access links in the default
case.
resource ScriptElementInfo(40)
{
kCoreScriptManagerBoss,
kInDesignAllLanguagesFS, k_Wild,
{
//For core clients, only the Document
// has a links collection
Provider
{
kLinkScriptProviderBoss,
48
#10082 Making Your Plug-in Scriptable
References
{
SurrogateParent
{
kDocumentObjectScriptElement
},
RepresentObject
{
kLinkObjectScriptElement
},
CollectionEvent
{
kCountEventScriptElement
},
}
}
Property
{
kItemLinkPropertyScriptElement,
p_Link,
"item link",
"Link to a placed file",
ObjectType( kLinkObjectScriptElement ),
{}
kNoAttributeClass,
}
//For core clients, there is a item link
// property on linkable objects
Provider
{
kLinkScriptProviderBoss,
{
Object
{
kStoryObjectScriptElement
},
Object
{
kGraphicObjectScriptElement
},
Property
{
kItemLinkPropertyScriptElement,
kReadOnly
},
}
}
}
#10082 Making Your Plug-in Scriptable
49
#10082 Making Your Plug-in Scriptable
References
} ;
resource ScriptElementInfo(41)
{
kMyScriptManagerBoss,
kInDesignAllLanguagesFS, k_Wild,
{
//For my client, there is a links
// collection on linkable objects
Provider
{
kLinkScriptProviderBoss,
{
Parent
{
kStoryObjectScriptElement
},
Parent
{
kImageObjectScriptElement
},
Parent
{
kEPSObjectScriptElement
},
Parent
{
kWMFObjectScriptElement
},
Parent
{
kPICTObjectScriptElement
},
Parent
{
kPDFObjectScriptElement
},
RepresentObject
{
kLinkObjectScriptElement
},
CollectionEvent
{
kCountEventScriptElement
},
}
}
//For my client, there is no item link
50
#10082 Making Your Plug-in Scriptable
References
// property on linkable objects
Provider
{
kNotSupported,
{
Object
{
kStoryObjectScriptElement
},
Object
{
kGraphicObjectScriptElement
},
Property
{
kItemLinkPropertyScriptElement,
kReadOnly
},
}
}
}
} ;
APIs
Interfaces
The following is a listing of commonly used scripting related interfaces. For reference
documentation, you can either search in {SDK}/docs/references/index.chm for the specified
interface by name, or if you have uncompressed the HTML-based API documentation
(sdkdocs.tar.gz), you can view {SDK}/docs/references/api/class{InterfaceName}.html with
your HTML browser.
•
IScript: {SDK}/public/interfaces/architecture/IScript.h
•
IScriptEventData: {SDK}/public/interfaces/architecture/IScriptEventData.h
•
IScriptProvider: {SDK}/public/interfaces/architecture/IScriptProvider.h
•
IScriptRequestHandler: {SDK}/public/interfaces/architecture/IScriptRequestHandler.h
•
IScriptUtils: {SDK}/public/interfaces/architecture/IScriptUtils.h
Classes
The following is a listing of commonly used scripting related partial implementation classes,
which you can inherit from in your implementations.. For reference documentation, you can
either search in {SDK}/docs/references/index.chm for the specified class by name, or if you
have uncompressed the HTML-based API documentation (sdkdocs.tar.gz), you can view
{SDK}/docs/references/api/class{ClassName}.html with your HTML browser.
#10082 Making Your Plug-in Scriptable
51
#10082 Making Your Plug-in Scriptable
Appendix
•
CScript: {SDK}/public/includes/CScript.h
•
CScriptEventData: {SDK}/public/includes/ CScriptEventData.h
•
CScriptProvider: {SDK}/public/includes/CScriptProvider.h
Other Headers
•
ScriptingID.h: {SDK}/public/interfaces/architecture/ScriptingID.h. This header is needed
in .fr files for base boss class IDs declarations.
•
ScriptInfoTypes.(f)h: {SDK}/public/includes/ScriptInfoTypes.(f)h
•
ScriptingDefs.h: {SDK}/public/includes/ScriptingDefs.h
•
ScriptingWindowDefs.h: {SDK}/public/includes/ScriptingWindowDefs.h
Appendix
How did the Scripting Architecture Change from InDesign/InCopy 2.x to InDesign
CS/InCopy CS?
The InDesign/InCopy 2.x Scriptable Plug-in Architecture
In InDesign/InCopy 2.x, a scriptable plug-in contained all of the pieces necessary to support a
script based operation.
•
All of the script providers
•
IScript implementations for every object
•
The core scripting architecture
Information provided to the external (scripting) clients about what objects, properties, and
events are available were provided by edge resources, which were platform-specific (AETE
resource for AppleScript and ODL resource for Visual Basic).
The Problems
The 2.x architecture did allow for internal and external plug-ins to provide scripting support,
but it wasn't very flexible. In particular:
52
#10082 Making Your Plug-in Scriptable
Appendix
•
The script providers were language neutral in theory, but since only two languages were
supported (AppleScript and Visual Basic), a lot of assumptions were made in the code.
•
Metadata about objects, properties, and events (names, types, descriptions, etc.) were
trapped in script providers' code and edge resources.
•
It was difficult to add or remove additional elements.
•
It was difficult to provide a localized scripting experience, as it required building separate
versions of plug-in for InDesign's Japanese feature set and for InCopy.
•
The feature coverage was incomplete.
The Refactored InDesign CS/InCopy CS Scripting Architecture
To solve the above problems, the InDesign CS/InCopy CS Scripting Architecture was
refactored in the following ways:
•
The core scripting architecture was consolidated in a required plug-in (Scripting).
•
Scripting language support was moved into an optional edge plug-in (Support for
AppleScript, Support for Visual Basic).
•
The scripting feature support was made easier to implement within the feature's (model)
plug-in.
•
Metadata about objects, properties, and events are now stored in a single ODFRC
resource.
•
APIs to access to metadata about objects, properties, events have been improved.
The benefits provided by the improved architecture are:
•
Consistent information about the DOM is published to all clients of the scripting
architecture.
•
Edge code when extending to other languages, such as JavaScript has been reduced.
•
The new architecture is easily reconfigurable (i.e. If the underlying feature is removed, so
is the DOM support).
The refactoring of the scripting architecture was originally conceived with the primary
motivation to make the scripting architecture easier to extend, but as a side benefit, it enabled
other technologies and features to be built on top.
#10082 Making Your Plug-in Scriptable
53
#10082 Making Your Plug-in Scriptable
Appendix
•
Saving a document in the InDesign Interchange file format for end users, the Package for
GoLive feature, third party integrators.
•
JavaScript support.
Using The TypeInfoConverter Tool
The TypeInfoConverter tool, located at {SDK}/tools/typeinfoconverter/TypeInfoConverter.exe,
is a convenient tool that facilitates the process of converting scriptable plug-ins developed for
InDesign/InCopy 2.x to the new and improved InDesign CS/InCopy CS architecture.
Specifically, this tool generates the Object, Property, Event, and Enum sections to the
ScriptElementInfo resource, and also creates entries for the Provider section of the
ScriptElementInfo resource. All scriptable plug-ins in the application have been converted
using this tool. This section gives a brief user guide for this tool, and discusses some caveats.
NOTE: Since this tool was written in Visual Basic, this tool is supported only on the Windows
platform.
Using The Tool For The First Time
1.
Run {SDK}/tools/typeinfoconverter/TypeInfoConverter.exe.
2.
The first time you run this tool, you will see this dialog that asks for a Type Library File:
Specify the path to a scriptable plug-in from InDesign/InCopy 2.x, such as
CustomPrefsScript.pln, as shown in the screen shot.
54
#10082 Making Your Plug-in Scriptable
Appendix
3.
This is followed by another dialog that asks for a Scripting Definitions File:
Specify the path to the .h file for your InDesign/InCopy 2.x scriptable plug-in project
where the script related IDs were defined.
4.
Once you have specified the type library and definitions file paths, you will see the main
dialog:
Note that the various objects, properties, and events that were exposed from the
scriptable plug-in have all been detected. Click on the different objects, properties and
#10082 Making Your Plug-in Scriptable
55
#10082 Making Your Plug-in Scriptable
Appendix
events to verify that the definitions are as you originally defined them in your scriptable
plug-in project.
5.
Click on the "Open ODFRC File". This will open another file dialog box. Specify the path
to the new .fr file you will be writing for the InDesign CS/InCopy CS version of your
plug-in. (If you don't have one yet, just cancel out of the file dialog box.) After you select
your .fr file (or cancel), you will see another window to the right titled "ODFRC File
Browser", which looks like this:
6.
To generate the Object section of a ScriptElementInfo resource, select one of the items in
the Object frame, and click on the "Write One Object" button. You will see something like
this be added into the ODFRC File Browser:
Object
{
kCPrefsObjectScriptElement,
c_CPrefs,
"CPrefs",
"Adobe InDesign 2.0 CustomPrefs Object",
kCPrefs_CLSID,
NoCollectionInfo,
k/*?*/IDBasedObjectScriptElement,
k/*?*/SuiteScriptElement,
}
RepresentObject{ kCPrefsObjectScriptElement },
The part from the word "Object" to the closing curly bracket ("}") is the part you will add
within your ScriptElementInfo resource for this object. The last line, that starts with
RepresentObject, will be added to the Provider section of your ScriptElementInfo
resource.
56
7.
To generate the Property, Event, or Enum sections of a ScriptElementInfo resource, select
one of the items in the Properties, Events, or Enums frames, and click on the "Write One
Property", "Write One Event", or "Write One Enum" button. You will see ODFRC File
Browser being augmented with sections of the ScriptElementInfo resource, similar to
what is shown above.
8.
When you have generated ScriptElementInfo sections for all the necessary parts of your
script objects, you can save the ODFRC sections in the ODFRC File Browser to a text file.
It is recommended that you save this to a temporary file using the File > Save As menu
item, so you can place the contents where you want to.
#10082 Making Your Plug-in Scriptable
Appendix
9.
When you are done, click the "Quit" button on the main dialog.
Running The Tool The Second Time And Onwards
The TypeInfoConverter tool remembers your selections for the Type Library and the Scripting
Definitions files.
•
If you want to change the Type Library file to examine, click on the "Open Type Library..."
button.
•
If you want to change the Scripting Definitions file to examine, click on the "Open
Definitions File..." button.
(The selections are stored in the Windows Registry under the key
HKEY_CURRENT_USER\Software\VB and VBA Program Settings\TypeLibBrowser\Files as
string keys.)
Caveats
Since this tool was written, the ScriptElementInfo format has changed slightly. You can either
hand-edit the generated ScriptElementInfo sections while pasting them into your .fr file. Please
refer to the References section in this technical note for the latest resource definitions and field
descriptions.
If you find any other problems with this tool, please contact the InDesign/InCopy Developer
Support Team at the Adobe Solutions Network.
Additional Resources For Further Understanding
•
Microsoft Developers Network (MSDN) page on COM: http://msdn.microsoft.com/com/
•
Apple Developer Connection (ADC) page on AppleScripting:
http://developer.apple.com/techpubs/macosx/Carbon/interapplicationcomm/AppleScrip
t/applescript.html
•
Adobe page on “Scripting with InDesign”:
http://www.adobe.com/products/indesign/scripting.html
#10082 Making Your Plug-in Scriptable
57
#10082 Making Your Plug-in Scriptable
Appendix
58