cScript ist eine von WERK II implementierte und in die priint:comet Plug-Ins und comet_pdf integrierte Programmiersprache.

Die Syntax von cScript lehnt sich an die der Programmiersprache C an. Dieses Dokument beschreibt den generellen Aufbau und die Verwendung von cScript. Informationen zu den implementierten Funktionen finden Sie hier.

InDesign® verfügt über exzellente Skript-Schnittstellen: Mit Hilfe eines integrierten Objektmodels, der sogenannten InDesign® Scripting DOM (Dokument-Objekt-Model) kann InDesign® gleich mit drei Skriptsprachen gesteuert werden:

Die Skriptsprachen bilden dabei die Basis der Programmierung. Die Scripting DOM enthält - in der jeweiligen Syntax - alles, was zur Bearbeitung von InDesign®-Dokumenten nötig ist. Eine sehr gute Online-Referenz dazu finden Sie hier.

Plug-in-Entwickler wie WERK II können die Scripting DOM um eigene Objekte und Funktionen erweitern und damit den Skriptschnittstellen die Funktionalitäten ihrer eigenen Plug-ins zur Verfügung stellen. Hier finden Sie eine vollständige Doku der InDesign® Scripting DOM Extensions der priint:comet Plug-in. Die Erweiterungen der Standard-InDesign® Scripting DOM sind in allen drei integrierten Skriptsprachen verfügbar. Ein AppleScript-Beipiel der priint.comet Scripting DOM Extension finden Sie hier.

Alle drei oben genannten Skriptpsprachen haben eins gemeinsam: Sie sind hauptsächlich dafür gemacht, InDesign® von außen zu steuern. Die priint:comet Platzhalter ändern das Dokument aber von innen.

Es ist natürlich möglich, die integrierten Skriptschnittstellen auch intern aufzurufen. Dazu muß aber jedesmal zuerst eine neue Instanz einer "Maschine" zum Ausführen des Skriptes gestartet werden.

Angenommen, ein Skript zum Laden eines Platzhalters wäre in JavaScript geschrieben. Dann ergäbe sich eine Situation wie im Bild dargestellt:

Jeder Aufruf eines Platzhalter-Skriptes hätte also den Start einer neuen JavaScrpit-Maschine zur Folge. Und in jedem dieser Skripte muß die Zielstelle der Bearbeitung neu ermittelt werden. Will man das vermeiden und trotzdem die Fexibilität skriptbasierter Platzhalter behalten, bleibt nur eine eigene Skriptsprache: Deshalb cScript.

Natürlich wird, wie Sie weiter unten sehen werden, auch für jedes cScript eine eigene "Maschine" gestartet. Aber diese Maschinen sind wesentlich kleiner und können entsprechend schneller geladen werden. Und: Sie arbeiten gewissermaßen innerhalb von InDesign® und "kennen" das jeweilige Ziel bereits. Damit ein Textplatzhalter seinen Inhalt ändern kann, ist zum Beispiel lediglich eine Zeile nötig:

textmodel::replace ("neuer Text");

5:1 cScript ist um einiges schneller als JavaScript mit InDesign® Scripting DOM. In einem Test haben haben wir 900 Rahmen eingefärbt. JavaScript hat dafür 2.355 ms benötigt. cScript hat die gleiche Aufgabe in 452 ms bewältigt. cScript war also ungefähr 5 mal schneller! Hier die beiden Testfunktionen:

cScript als Sprache ist unabhängig von InDesign® und in andere Softwarekomponenten integriert werden. cScript kann daher problelos von comet_pdf verwendet werden.

Achtung: Dass cScript in comet_pdf verwendet werden kann, heißt nicht, dass dort auch alle Skriptfunktionen 1:1 verwendet werden können! Welche der Funktionen im Einzelnen verfügbar sind, entnehmen Sie bitte der entsprechenden cScript-Doku.

Wir bemühen uns, cScript so umfangreich zu gestalten, dass alle Standardaufgaben damit gelöst werden können. Trotzdem ist cScript kein vollständiger Ersatz für InDesign®-Javascript. Mit der Dolmetscher-Funktion run_javascript kann JavaScript auch aus cScript heraus ausgeführt. Umgekehrt bieten die Funktionen app.comet.exec und app.comet.eval die Möglichkeit, aus JavaScript (oder AppleScript oder VBScript) heraus cScript-Programme auszuführen:

Skripte in cScript werden aus einer Untermenge der Schlüsselworte der Programmiersprache C gebildet und entsprechen weitgehend der dort verwendeten Syntax. Der Funktionsumfang der Sprache cScript ist speziell auf die Erfordernisse der priint:comet Komponenten zugeschnitten. Allgemein gilt :

Erkennt ein C-Compiler in einem cScript syntaktische Fehler, ist das Skript falsch. Aber von einem C-Compiler als fehlerfrei erkannter Quellcode muss nicht cScript-konform sein.

Hier finden Sie eine Beschreibung der Grammatik von cScript.

Literaturtips zu C sind in großer Zahl zu finden. Hier zwei davon:

Das Beispiel zeigt das Programm Hello World in cscript

Zerige das InDesign®-Nachrichtenfenster mit dem Text Hello World. Comet_pdf schreibt die Nachricht in die Konsole.

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

Die Bearbeitung eines cScript Skriptes wird immer bei der Funktion main begonnen. Die Funktion muss vom Typ int sein und darf (anders als in C) keine Funktionsparameter haben.

int main ()

Enthält ein Skript diese Funktion nicht, wird es nicht ausgeführt.

Ein häufiger Fehler im Hauptprogramm ist ein fehlendes return am Ende der Funktion. Bei der Skriptausführung durch die Plugins wird das Ergebnis der Ausführung (der Wert von return) abgefragt. Fehlt die Angabe, ist dieser Wert zufällig und möglicherweise schliesst das aufrufende Plugin aus diesem zufälligen Wert, dass das Skript fehlerhaft beendet wurde.

Die folgende Tabelle enthält eine vollständige alphabetische Aufzählung aller in C definierten Schlüsselwörter. Die durchgestrichenen Worte sind in cScript nicht definiert. Erschrecken Sie nicht, dass so viel durchgestrichen ist, auch mit dem Rest können Sie theoretisch alles programmieren. Und schliesslich ist ja auch der Sprachumfang von C selbst recht klein und reicht doch, so komplexe Programme wie InDesign® und die priint:comet Plugins zu schreiben.

Schlüsselwort Beschreibung
auto

Speicherklassen werden von cscript nicht unterstützt

break
case

switch-case Konstruktionen sind in cscript nicht enthalten.

char
continue

In for- und while-Schleifen muss continue mit Hilfe von if-else realisiert werden.

default

switch-case Konstruktionen sind in cScript nicht enthalten.

do

Do-while Schleifen werden von cScript nicht unterstützt

double
else
enum

In cscript können keine eigenen Datentypen definiert werden.

extern

Speicherklassen werden von cscript nicht unterstützt.

float
for
goto
if
int
long

wie int

register

Speicherklassen werden von cscript nicht unterstützt.

return
short

wie int

sizeof
static

Speicherklassen werden von cScript nicht unterstützt.

struct

In cScript können keine eigenen Datentypen definiert werden.

switch
typedef

In cscript können keine eigenen Datentypen definiert werden.

union

In cscript können keine eigenen Datentypen definiert werden.

unsigned
void
volatile

Speicherklassen werden von cscript nicht unterstützt.

while

cScript akzeptiert neben C-Kommentaren (/* */) auch die in C++ verwendeten //-Kommentare, die immer bis zum Zeilenende gelten. /* */- Kommentare dürfen, wie in C und C++ nicht geschachtelt werden.

/* 
    Mehrzeiliger Kommentar. 
	Die Kommentare können nicht geschachtelt werden!
*/

// Kommentar bis zum Zeilenende

Skripte werden wie eigene kleine Apps ausgeführt. Jedes Skript verfügt während seiner Ausführung innerhalb des Arbeitsspeichers über einen eigenen zusammenhängenden Speicherbereich fester Größe. Nach der Ausführung des Skriptes wird dieser Speicher wieder freigegeben. Die Größe des benötigten Speichers hängt wesentlich von der Länge des Skriptes und der Anzahl und Größe der verwendeten Variablen ab.

Der Skriptspeicher besteht im wesentlichen aus drei Teilen:

In Abhängigkeit von den verwendeten Symbolen (Stack) und deren Daten (Heap) bewegen sich Stack und Heap während der Laufzeit des Skriptes aufeinander zu. Wird der Gültigkeitsbereich eines Bezeichners verlassen, werden das Symbol und dessen statische Daten wieder entfernt. Achtung : Mit alloc-Funkionen reservierter Speicher wird dabei nicht automatisch freigeben.

Zur Laufzeit können Stack und Heap aufeinander zuwachsen (und sich wieder voneinander entfernen). Treffen beide aufeinander, gibt es den berühmten Stack overflow. Dann war der verfügbare Skriptspeicher zu klein.

Sie können die Größe des Skriptspeichers in der Palette CometXML unter dem Punkt cScript-Puffer in kB und mit Hilfe des Skriptbefehles prefs::set_script_buffer einstellen. Standardwert ist 512 kB.

Achtung : Wie Sie weiter unten sehen werden, ist es mit Hilfe der alloc-Fubkionen möglich, innerhalb eines Skriptes Arbeitsspeicher auch außerhalb des Skriptspeichers zu reservieren. Nicht freigegebener Speicher wird zum Ende des Skriptes automatisch wieder freigegeben. Es ist aber guter Stil, reservierten Speicher auch selbst wieder freizugeben.

Die Sprache cScript kennt vier Datentypen: int, float, char und String.

Datentyp Beschreibung
int, short, long

Ganzzahlen, 8 Byte (also 64bit). Die Angaben sind gleichwertig.

float, real, double

Kommazahlen, 8 Byte. Die Angaben sind gleichwertig.

char

1 Byte

String Eingebauter Datentyp für Zeichenketten. String ist eine Adresse und muß mit string::alloc allokiert werden.

Die C-Schlüsselworte unsigned, typedef, enum und struct sind in cScript nicht definiert. Es können keine eigenen Typdefinitionen gemacht werden.

Zur besseren Lesbarkeit Ihrer Skripte gibt es zusätzlich eine Reihe modulspezifischer Typnamen. Alle diese Datentypen sind Pointer vom Typ int*. Hier finden Sie eine Liste aller definierten Datentypen.

Arrays sind zusammenhängende Speicherbereiche mit einer festen Anzahl von Elementen des gleichen Datentypes. Die häufigste Anwendung von Arrays sind statische Zeichenketten (z.B. char[512]).

Die Größe des reservierten Speicher eines Arrays kann zur Laufzeit nicht mehr geändert werden. Speicherfreigaben mit release auf eine Array-Variable führen zu schweren Fehlern und können InDesign® zum Absturz bringen!

Arrays werden durch die Angabe des Datentypes, eines Namens und der Anzahl der Elemente in eckigen Klammern [] vereinbart. Das erste Element des Arrays wird bereits bei der Deklaration mit 0 (bzw. 0,0 bei floats) initialisiert. Alle weiteren Elemente bleiben undefiniert.

Da Datentypen unterschiedlich breit sein können, hängt die tatsächliche Länge eines Arrays im Arbeitsspeicher nicht nur von der Anzahl der Elemente sondern auch vom Datentyp ab.

Hier zwei Beispiele für Arrays

int data [16];	//Array mit 16 Zahlen (16*8=128 Bytes auf dem Heap)
char s [16];	//Array für 16 Ascii-Zeichen (16*1=16 Bytes auf dsem Heap)

Der Zugriff auf Elemente eines Arrays erfolgt über den Index des gewünschten Elementes. Der Index bezeichnet dabei das Element, nicht das Byte! Das erste Element hat den Index 0. Der Index wird in eckigen Klammern angegeben. Alternativ zu den eckigen Klammern kann auch folgende Schreibweise verwendet werden:

array [idx] entspricht *(array+idx)

Zugriffe auf nicht initialisierte Werte können zu unerwarteten Ergebnissen führen.

Der Aufruf zeigt das zweimal das vierte Zeichen char*Strings s:

int main ()
{
	char s [16];

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

	return 0;
}

Deklaration und Füllen eines int-Arrays. Die Elemente bekommen als Wert jeweils ihren doppelten Index.

int data [16];
int k;

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

TIPP : Es ist gute Praxis, for-Schleifen über Arrays immer von 0 bis Länge-1 zu machen.

In cScript kann mit globalen Variablen gearbeitet werden. Globale Variable werden über die Palette Einstellungen angelegt und geändert.

Bitte beachten Sie, dass globale Variablen vom PubishingServer nicht unterstützt werden!

In Skriptem können globale Variablen wie jede andere Variable auch verwendet werden. Folgenden Datentypen werden unterstützt :

Die Variablen werden gelesen, wenn eine Verbindung zum Datenpool hergestellt wurde. Jede InDesign-Instanz verwendet dabei eine eigene Kopie der Daten. Wertänderungen an den Variablen gelten immer nur lokal. Mit der Skriptfunktion system::commit_global können Änderungen der Werte in den Datenpool zurückgeschrieben werden. Lokal geänderte Variablen werden in der Palette Einstellungen rot markiert.

Eine neue globale Variable legen Sie mit an. Sie werden in einem Dialog nach Name, Beschreibung und Typ der Variable gefragt. Nach Klick von OK erscheint ein Fenster, in dem Sie den Wert der neuen Variablen angeben können.

Die Beschreibung wird in der Palette Einstellungen als Tooltip gezeigt des Variablennamens gezeigt.

Zum Ändern der Einträge verwenden Sie das Button in der ersten Zeile der Liste. Der Tooltip der Buttons gibt Ihnen weitere Informationen.

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

Die ID wird zur Bestimmung des Datentypes der Variable benutzt: :

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

Die ID kann nicht als Primärschlüssel der Tabelle globals verwendet werden! Verwenden Sie hier die Kombination id/alias als Primärschlüssel :

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

alias varchar (255)

Eindeutiger Name der Variable. Namen müssen mit einem Buchstaben oder _ beginnen und dürfen nur Buchstaben, Ziffern und _ enthalten. Die Verwendung unterscheidet Groß- und Kleinschreibung.

path text

Wert der Variable. Bei Zahlen muss der Text einen entsprechenden Zahlenstring enthalten. Als Dezimaltrenner bei float wird der Punkt (.) erwartet.

description text

Beschreibung der Variable. Der Text wird in den Hilfetexten der Palette Einstellungen gezeigt.

enabled int

Der Wert wird bisher ignoriert.

Es können bis zu 8192 globale Variable definiert werden.

 

 

Bitte haben Sie etwas Geduld. Diese Datei ist zur Zeit in Bearbeitung.

Beachten Sie aber bitte in jedem Fall die folgenden wichtigen Hinweise:

 

main als Einstiegspunkt
alloc/release Balance
64 Bit
Auswertung von Ausdrücken von hinten nach vorne
Bedingungsanweisungen
float braucht Kommazahl

Statische String/char*-Rückgaben werden in drei Fällen gemacht:

  1. Zum Ermittlen globaler Werte (z.B. in serror, file::get_nth, book::get_master, ...). Die Rückgabewerte sind genau einmal definiert. Jeder weitere Aufruf an die Funktion überschreibt das Ergebnis.
  2. Zum Lesen von Eigenschaften von Script-Objekten (z.B. string::get, link::crossref::name, ...). Die Rückgabewerte sind so lange definiert wie das Objekt, dessen Eigenschaft geholt wurde.
  3. In Funktionen mit einem Output-Parameter vom Typ String kann der char*-Zeiger des String-Parameters zurückgegeben werden (z.B. frame::gettext). Dieser Wert ist solange definiert, wie die Variable, die im Aufruf benutzt wird, nicht verändert wird.

Für die Funktionsergebnisse muss im Aufruf kein Speicherplatz reserviert werden. Die Funktionen geben entweder den Wert einer globalen Adresse oder einer Adresse im befragten Objekt zurück. Folgende Dinge müssen deshalb unbedingt beachtet werden :

Außer bei der Verwendung als Funktionsparameter sollten die Ergebnisse von Funktionen mit statischen String/char*-Rückgabewerten immer in lokale Variablen kopiert werden. Eine einfache Adress-Zuweisung an eine entsprechende lokale Variable genügt nicht - weil Sie dann ja nur die Adresse des Ergebnisses behalten (die sich außerdem, eben weil statische Variable, sowieso nicht ändern wird).

Verwendung als Funktionsparameter

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

Direktes Kopieren in eine Skriptvariable

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

Kopieren in eine Skriptvariable

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

Fehler in der Verwendung globaler Variablen sind schwer zu erkennen. Hier eine kleiner Sammlung häufiger Fehler, die Sie unbedingt vermeiden sollten.

Der Rückgabewert darf nicht an allokierte Variablen zugewiesen werden. Die Variable ändert dadurch ihren Wert (Adresse) und zeigt damit auch nicht mehr auf den von Ihnen reservierten Speicherbereich. Mit alloc reservierter Speicher kann mit release nicht mehr freigegeben werden.

char * str = alloc (1024); // oder char str [1024]
:
str = file::get_nth ("$DESKTOP", 0); // FALSCH! str ist eine allokierte Variable.

Die Funktionen dürfen nicht an Stellen verwendet werden, an denen ihr Inhalt geändert wird (wie etwa im ersten Parameter von strcat oder strcpy).

strcat (file::get_nth ("$DESKTOP", 0), "pp"); // FALSCH! Der Wert des Ergebnisses wird verändert.

Auch über den (weniger offensichtlichen) Weg einer definierten lokalen Variable dürfen die Inhalte nicht verändert werden.

char * str;
:
str = file::get_nth ("$DESKTOP", 0); // Bis hierher ist noch alles richtig
strcat (str, "pp");  // FALSCH! str ist eine statische Variable

Variablen, deren Wert durch eine Zuweisung an eine r/o-Ergebnis-Funktion definiert wurde, dürfen nicht gelöscht werden. (verbotene Variablenänderung und freigeben von nicht selbst allokiertem Speicher).

char * str;
:
str = file::get_nth ("$DESKTOP", 0); // Bis hierher ist noch alles richtig
:
release (str); // FALSCH! r/o Variable darf nicht gelöscht werden. 

Nachfolgende Aufrufe einer Funktion mit r/o-Rückgabe kann das Ergebnis von Vorgängeraufrufen der gleichen Funktion überschreiben. Verwenden Sie deshalb solche Funktionen nie mehrfach innerhalb einer Anweisung.

wlog ("",
	"'%s', '%s'\n",
	file::get_nth ("$DESKTOP", 0),
	file::get_nth ("$DESKTOP", 1)); // FALSCH! Ergebnis von get_nth wird mglw. überschrieben

Nachfolgende Aufrufe einer Funktion mit r/o-Rückgabe kann das Ergebnis von Vorgängeraufrufen der gleichen Funktion überschreiben. Verwenden Sie deshalb solche Funktionen nie mehrfach innerhalb einer Anweisung.

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); // FALSCH! str wurde mittlerweile gelöscht!

Strings sind Zeiger! Funkionen mit einem String als Rückgabewert dürfen diese Variable keinesfalls selbst allokieren: Mit dem Verlassen einer Funktion ist auch die Gültigkeit lokalen Variablen beendet, der Rückgabewert der Funktion ist also nach dem Ende der Funktion selbst auch nicht mehr gültig. Die Lösung ist, den String außerhalb der Funktion zu allokieren und der Funktion als Zusatz-Parameter mitzugeben. Damit ist der String sicher definiert und kann trotzdem als bequemer Rückgabewert verwendet werden.

Hier die gültige und gefahrlose Definition einer String-Funktion:

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

	return result;
}

Die Aufrufstelle ist für die Variable zuständig:

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

Das Gleiche wie für String-Funkionen gilt analog für alle 'Zeiger'-Typen wie ItemRef, XMLTree, Query, int*, usw. .