cScript is a programming language implemented by WERK II and integrated into the priint:comet plug-ins and comet_pdf.

The syntax of cScript is based on but not equal the C programming language. This document describes the general structure and usage of cScript. Information about the implemented functions can be found here.

InDesign® has excellent scripting interfaces: With the help of an integrated object model, the so-called InDesign® Scripting DOM (Document Object Model), InDesign® can be controlled with three scripting languages at once:

The scripting languages form only the basis of the programming. The Scripting DOM contains - in the respective syntax - everything that is necessary for editing InDesign® documents. A very good online reference to it can be found here.

Plug-in developers like WERK II can extend the Scripting DOM with their own objects and functions and extent the scripting interfaces with the functionalities of their own plug-ins this way. Here you can find a complete documentation of the InDesign® Scripting DOM Extensions of the priint:comet plug-ins. The extensions of the standard InDesign® Scripting DOM are available in all three integrated scripting languages. An AppleScript sample of the priint.comet Scripting DOM Extension can be found here.

All three scripting languages mentioned above have one thing in common: They are mainly made to control InDesign® from the outside. The priint:comet placeholders, however, change the document from the inside.

It is of course possible to call the integrated script interfaces internally as well. But to do this, a new instance of a "machine" must first be started each time to execute the script.

Suppose a script to load a placeholder was written in JavaScript. This would result in a situation like the one shown in the image:

So every call of a placeholder script would result in the start of a new JavaScrpit machine. And in each of these scripts the target location of the processing must be determined anew. If you want to avoid this and still keep the flexibility of script-based placeholders, you have to use your own scripting language: Therefore cScript.

Of course, as you will see below, a separate "machine" is also started for each cScript. But these machines are much smaller and can be loaded correspondingly faster. And: To a certain extent, they work within InDesign® and already "know" the respective target. For example, for a text placeholder to change its content, only one line is needed:

textmodel::replace ("New Text");

5:1 cScript is a lot faster than JavaScript with InDesign® Scripting DOM. In a test we colored 900 frames. JavaScript took 2,355 ms to do this. cScript did the same task in 452 ms. So cScript is about 5 times faster! Here are the two test functions:

cScript is independent of InDesign® and can be integrated into other software components. Therefore, cScript can be used without any problems by comet_pdf.

Attention: The fact that cScript can be used in comet_pdf does not mean that all script functions can be used there 1:1! Each function needs its own implementation depending on the host application anyway. Which of the functions are available in detail, please refer to the corresponding cScript documentation.

We strive to make cScript as comprehensive as possible so that all standard tasks can be solved with it. Nevertheless, cScript is not a complete replacement for InDesign® JavaScript. With the interpreter function run_javascript, JavaScript can also be executed from within cScript. Conversely, the app.comet.exec and app.comet.eval functions provide the ability to run cScript programs from JavaScript (or AppleScript or VBScript):

Scripts in cScript are formed from a subset of the keywords of the C programming language and largely correspond to the syntax used there. The functional scope of the cScript language is specially tailored to the requirements of the priint:comet components. In general :

If a C compiler detects syntactical errors in a cScript, the script is incorrect. But source code detected as error-free by a C compiler need not be cScript-compliant.

Here you can find a description of the grammar of cScript.

Literature tips on C can be found in large numbers. Here are two of them:

The example shows the Hello World program in cscript.

Show the InDesign® dialog window with the text Hello World. Comet_pdf writes the message to the console.

int main ()
{
    showmessage ("Hello World");
    return 0;
}

The processing of a cScript script is always started at the function main. The function must be of type int and must not have any function parameters (unlike in C).

int main ()

If a script does not contain this function, it will not be executed.

A common error in the main program is a missing return at the end of the function. When the script is executed by the plugins, the result of the execution (the value of the return) is queried. If missing, this value is random and possibly the calling plugin concludes from this random value that the script ended incorrectly.

The following table contains a complete alphabetical enumeration of all keywords defined in C. The words that are crossed out are not defined in cScript. Don't be alarmed that so much is crossed out, even with the rest you can theoretically program anything. And after all, the language scope of C itself is quite small too and yet sufficient to write such complex programs as InDesign® and the priint:comet plugins.

Keyword Description
auto

Storage classes are not supported by cscript

break
case

switch-case constructions are not included in cscript.

char
continue

In for and while loops, continue must be implemented using if-else.

default

switch-case constructions are not included in cScript.

do

Do-while loops are not supported by cScript

double
else
enum

extern

Storage classes are not supported by cscript

float
for
goto
if
int
long

Like int

register

Speicherklassen werden von cscript nicht unterstützt.

return
short

wie int

sizeof
static

Storage classes are not supported by cscript

struct

You cannot define your own data types in cScript.

switch
typedef

You cannot define your own data types in cScript.

union

You cannot define your own data types in cScript.

unsigned
void
volatile

Storage classes are not supported by cscript

while

cScript accepts not only C comments (/* */) but also the //comments used in C++, which always apply up to the end of the line. /* */- Comments must not be nested, as in C and C++.

/* 
    Multiline comment. 
	The comments cannot be nested!
*/

// Comment to end of line

Scripts are executed like their own little apps. Each script has its own contiguous memory area of fixed size within the working memory during its execution. After the execution of the script, this memory is released again. The size of the required memory depends essentially on the length of the script and the number and size of the variables used.

The script memory consists essentially of three parts:

Depending on the symbols used (stack) and their data (heap), stack and heap move towards each other during the runtime of the script. If the definition scope of an identifier is left, the symbol and its static data are removed again from stack and heap. (Attention : Memory reserved with alloc functions is not automatically freed.) If stack and heap meet the available script memory is too small and you will get the famous Stack Overflow error.

The default size of the script buffer is 512 kB. With the help of panel Comet Admin -> Comet you can set the size of the buffer reservered for scripts by your needs. With prefs::set_script_buffer you can do the same also by cScript. But please note: 

1. Of course, the setting applies only to future scripts
2. The setting applies to all future scripts

The cScript language knows four basic data types: int, float, char and String.

Data Type Description
int, short, long

Integers, 8 bytes (i.e. 64bit). The specifications are equivalent.

float, real, double

Floating numbers, 8 bytes. The specifications are equivalent.

char

1 Byte

String

Built-in data type for strings. String is an address and must be allocated with string::alloc.

Again, the key words unsigned, typedef, enum and struct used in standard C are not defined in cScript. You cannot make your own type definitions.

For better readability of your scripts there is an additional set of module specific type names. All these data types are pointers of type int*. Here you can find a list of all defined data types.

Arrays are contiguous memory areas with a fixed number of elements of the same data type. The most common application of arrays are static strings (e.g. char[512]).

The size of the reserved memory of an array can no longer be changed at runtime. Memory releases with release on an array variable lead to serious errors and can crash InDesign®!

Arrays are declared by specifying the data type, a name and the number of elements in square brackets [] . The first element of the array is already initialized with 0 (or 0,0 for floats) at declaration. All other elements remain undefined.

Since data types can have different widths, the actual length of an array in memory depends not only on the number of elements but also on the data type.

Here are two examples of arrays

int data [16];	//Array with 16 ints (16*8=128 bytes on heap)
char s [16]; //Array for 16 Ascii letters (16*1=16 bytes on heap)

The access to elements of an array is done via the index of the desired element. The index designates the element, not the byte! The first element has the index 0. The index is specified in square brackets. Alternatively to the square brackets the following notation can be used:

array [idx] corresponds to *(array+idx)

Accesses to uninitialized values can lead to unexpected results.

The call shows the twice the fourth character of the char* string s:

int main ()
{
	char 	s [16];

	strcpy (s, "Hello world");
	showmessage ("%c ?= %c", s[4], *(s+4));

	return 0;
}

Declaration and filling of an int array. The elements get their double index as value.

int data [16];
int k;

for (k = 0; k < 16; k++)
{
	data [k] = k*2;
}

TIPP : It is good practice to make for loops over arrays always from 0 to length-1.

In cScript it is possible to work with global variables. Global variables are created and changed using the Comet ->Settings panel.

Please note that global variables are not supported by the PubishingServer!

In scripts global variables can be used like any other variable. The following data types are supported:

The variables are read when a connection to the data pool has been established. Each InDesign instance uses its own copy of the data. Value changes to the variables always apply locally only. The script function system::commit_global can be used to write changes to the values back to the data pool. Locally changed variables are marked red in the Settings panel.

With the ýou can create a new global variable You will be asked for name, description and type of the variable in a dialog. After clicking OK, a window appears in which you can specify the value of the new variable.

The description is shown as tool tip in the Settings panel.

To change the entries of global variables use the button in the first line of the list. The tooltip of the buttons gives you more information.

Die in der Liste Einstellungen gezeigten Einträge können natürlich auch direkt über die Datenquelle konfiguriert werden. Die Definitionen der Variablen und ihre Werte werden in der Datei datafiles.xml (XML und SOAP) bzw. in der Tabelle globals (ODBC und Oracle) abgelegt. Die folgende Tabelle beschreibt die nötigen Attribute von datafiles und globals.

Name Typ Beschreibung
id int

The ID is used to determine the data type of the variable: :

  -1 : char [8192]
  -2 : float
  -3 : int

The ID cannot be used as primary key of the table globals! Use the combination id and alias as primary key here:

ALTER TABLE `globals` ADD PRIMARY KEY ( `id` , `alias` )

alias varchar (255)

Unique name of the variable. Names must start with a letter or _ and may contain only letters, digits and _. The usage is case sensitive.

path text

Value of the variable. For numbers, the text must contain a corresponding number string. The dot (.) is expected as decimal separator for floats!

description text

Description of the variable. The text is shown in the tool tips of the Settings panel.

enabled int

Ignored

Up to 8192 global variables can be defined.

 

 

Please be patient. This file is currently under construction.

However, please note the following important information in any case:

 

main As Entry Point for Every cScript
alloc/release Balance
64 Bit
Evaluation of Expressions from Back to fFont
If Conditions
float needs Floting Point Numbers

Static function returns are made in three cases:

  1. For determining global values (e.g. in serror, file::get_nth, book::get_master, ...). The return values are defined exactly once. Any further call to the function overwrites the result.
  2. For reading properties of script objects (e.g. string::get, link::crossref::name, ...). The return values are defined as long as the object exists whose property was fetched.
  3. In functions with an output parameter of type String the char* pointer of the string parameter can be returned (e.g. frame::gettext). This value is defined as long as the variable used in the call is not changed.

No memory must be reserved for the function results in the call. The functions return either the value of a global address or an address in the queried object. The following things must therefore be observed :

Except when used as function parameters, the results of functions with static String/char* return values should always be copied to local variables. A simple address assignment to a corresponding local variable is not sufficient - because then you only keep the address of the result (which, in addition, will not change anyway - because it's static, you remember?).

Use as function parameter

showmessage ("%s", file::get_nth ("$DESKTOP", 0));

Copy directly to a script variable

String 	str = string::alloc (file::get_nth ("$DESKTOP", 0));

Copy to a script variable

String 	str = string::alloc ();
:
:
string::set (str, file::get_nth ("$DESKTOP", 0));

Errors in the use of global variables are difficult to detect. Here is a small collection of common mistakes that you should avoid at all costs.

The return value must not be assigned to allocated variables. The variable thereby changes its value (address) and thus also no longer points to the memory area you reserved. Memory reserved with alloc can no longer be released with release.

char * str = alloc (1024); // oder char str [1024]
:
str = file::get_nth ("$DESKTOP", 0); // ERROR! str is an allocted variable

The functions must not be used in places where their content is changed (such as in the first parameter of strcat or strcpy).

strcat (file::get_nth ("$DESKTOP", 0), "pp"); // ERROR! The result will be changed

Also via the (less obvious) way of a defined local variable the contents must not be changed.

char * str;
:
str = file::get_nth ("$DESKTOP", 0);	// Up to here everything is still correct
strcat (str, "pp");						// FALSE! str is a static variable

Variables whose value was defined by an assignment to a r/o result function must not be deleted. (forbidden variable change and release of not self-allocated memory).

char * str;
:
str = file::get_nth ("$DESKTOP", 0); 	// Up to here everything is still correct
:
release (str); 							// FALSE! r/o variable must not be deleted. 

Subsequent calls to a function with r/o return may overwrite the result of previous calls to the same function. Therefore, never use such functions more than once within a single statement.

wlog ("",
		"'%s', '%s'\n",
		file::get_nth ("$DESKTOP", 0),
		file::get_nth ("$DESKTOP", 1)); // FALSE! Result of get_nth may be overwritten

Subsequent calls of a function with r/o return may overwrite the result of predecessor calls of the same function. Therefore, never use such functions more than once within one statement.

String 		str = string::alloc ();
char 	* 	s1;
:
s1 = frame::get_text (gFrame, str, 0, kEnd);
:
wlog ("", "%s\n", s1);
string::release (str);
wlog ("", "%s\n", s1); // WRONG! str has been deleted in the meantime!

Functions with a String as return value must never allocate this variable themselves: When a function is exited, the validity of local variables is also terminated, so the return value of the function is also no longer valid after the function itself has ended.

Here is the valid and safe definition of a string function:

String my_string_func (String result)
{
	string::set (result, "your new value");

	return result;
}

The calling point is responsible for the variable:

String myStringstring::alloc ();
:
wlog ("", "%s\n", my_string (myString));
:
string::release (myString)

The same as for string functions applies analogously to all 'pointer' types like, int*, etc. .ItemRef, XMLTree, Query