Lade...
 

FrameMaker ExtendScript

public blog all about FrameMaker ExtendScript

Mittels der Funktion ''Find' des Doc-oder Book-Objekts (FM-Dokument/FM-Buch) kann ein Dokument oder auch ein Buch analog zur Suchfunktion in der Oberfläche durchsucht werden. Die Definition der Suchparameter führt aber wie im Adobe Forum(externer Link) immer wieder für Schwierigkeiten.
Im Gegensatz zum Handling von Open- oder Save-Paramertern, bei denen mittels
Open default parameter
var openProps = app.GetOpenDefaultParams() ;
oder
Savedefault parameter
var saveProps = app.GetSaveDefaultParams() ;
die Default-Parameterlister ermittelt werden können, müssen diese Parameter bei der Suchfunktion erstellt werden. Hierzu müssen zunächst die Objeke PropVals und PropVal erzeugt werden, bevor die Suchfunktion ausgeführt werden kann. Wie dies in der Praxis aussieht zeigt das folgende Snippet, welches das Dokument nach dem ersten Vorkommen des Texts "Test" durchsucht.
Suchfunktion
var doc = app.ActiveDoc;
var range = doc.TextSelection;
var propVals = new PropVals() ;

propVal = new PropVal() ;
propVal.propIdent.num = Constants.FS_FindText ;
propVal.propVal.valType = Constants.FT_String;
propVal.propVal.sval = "Test" ;
propVals.push(propVal);

//Add further search params here

//Execute find method
var findrange = doc.Find (range.end, propVals);

//Check if text found
if (findrange.beg.obj.ObjectValid())
   alert("Text found") ;
else
   alert("Text not found") ;
veröffentlicht am 13 02 2012
Drucken
Seit heute ist die erste Version unseres Plugins ((SWATFMINSTALLSCRIPTS|Install Scripts In Folders) verfügbar. Mit diesem Plugin ist es möglich eine Scriptbibliothek in einem zentralen Serververzeichnis abzulegen und alle Scripts beim Start von FrameMaker automatisch zu laden. Mit diesem Plugin verfügen alle Anwender im Unternehmen automatisch und aktuell über alle verfügbaren Scripts, ohne diese manuell auf den jeweiligen Clients installieren und aktualisieren zu müssen.
veröffentlicht am 27 12 2011
Drucken
ExtendScript basiert bekanntlich auf der objektorieten Skriptsprache JavaScript und erweitert diese um das jeweilige Objektmodell und um entsprechende Zugriffsfunktionen der entsprechende Adobe Application; i.d.F. Adobe FrameMaker.
Bei der Entwicklung entstehen zahlreiche Funktionen, die ich gerne direkt der jeweiligen Klasse zuordnen möchte, ich also objektorientiert entwickeln will.
Die Objektorientierung in JavaScript(externer Link) und damit in ExtendScript wird nicht über Klassen sondern über Prototypen realisiert. Und genau die Umsetzung von Prototypen setze ich dafür ein. Das folgende Snippet erweitert das Object BodyPage um eine neue Funktion IsEmpty():
Erweiterung des BodyPage Prototypen
  1 . app.ActiveDoc.FirstBodyPageInDoc;
  2 . BodyPage.prototype.IsEmpty = function()
  3 . {
  4 .    //your code here
  5 . }
Speziell ist hier sicherlich die Zeile 1. Der Prototyp BodyPage kann erst erweitert werden, wenn erstmals ein Objekt instanziiert wurde. Dies funktioniert in diesem Fall auch, wenn wir kein aktives Dokument haben (beim Start von FrameMaker), da ESTK immer ein Objekt, wenn auch invalide, liefert, auf dem gearbeitet werden kann. Wird dieses Snippet direkt in das Startup-Verzeichnis von FrameMaker gestellt, steht nun für alle Skripts die mit BodyPages arbeiten, die Funktion IsEmpty() zur Verfügung. Somit kann die Funktion nicht nur in einem konkreten Anwendungsfall genutzt werden, sondern steht global auch zur Verwendung durch andere Skripts zur Verfügung. Eine konkrete Implementierung finden Sie im Skript Leere Seiten beim Speichern löschen. Hier wird das Objekt Doc (für ein FM Dokument) und das Objekt BodyPage entsprechend erweitert.
veröffentlicht am 24 11 2011
Drucken
Wenn ein FrameMaker Dokument gespeichert wird, werden leere Seiten am Ende des Dokuments automatisch gelöscht, sofern dies unter Format > Page Layout > Pagination die Einstellung Before Saving and Printing auf Delete Empty Pages eingestellt wurde.
Dies ist besonders dann hilfreich, wenn sich beispielsweise die Anzahl Seiten durch die Implementierung der automatische Vorgabeseitenzuweisung(externer Link) verringert.
Dies funktioniert mit FrameMaker Bordmitteln aber nur dann, wenn die leeren Seiten mit den Standardseiten Rechts und/oder Links (engl. FM Right bzw. Left) aufgebaut wurden. Sind andere Vorgabeseiten zugewiesen, was nicht nur bei der Verwendung der automatischen Vorgabeseitenzuweisung Standard ist, löscht FrameMaker die leeren Seiten nicht.

Das Skript SWATDeleteEmptyPages sorgt nun dafür, dass auch diese Seiten beim Speichern eines Dokuments gelöscht werden. Es wird automatisch bei jedem Speichern ausgeführt und berücksichtigt dabei die oben beschriebene Einstellung. Ebenso wird berücksichtigt ob es sich um ein einseitiges- oder doppelseitiges Layout handelt.
Die Datei einfach in das FrameMaker Verzeichnis ($FMHome\startup) kopieren und FrameMaker ggf. neu starten. Im Skript je nach Anforderungen kann folgendes konfiguriert werden:
Konfigurationseinstellungen
  1 . var swatCheckDoubleSideLayout = true ; 
  2 . var swatMasterPageNameForLastPage = "";
In Zeile 1 wird angegeben ob das doppelseitige Layout berücksichtigt werden soll. Steht der Wert auf true endet das Dokument immer mit einer leeren Seite (Rechts oder Links je nach Pagination-Einstellung). Steht der Wert auf false werden immer alle leeren Seiten gelöscht.
In Zeile 2 kann optional eine Vorgabeseite angegeben werden, die der letzten leeren Seite zugewiesen wird. Dazu muss der Wert in Zeile 1 zusätzlich auf true stehen und das Dokument natürlich mit einer leeren Seite enden. Das Skript wurde für einen konkreten Anwendungsfall entwickelt. Deshalb kann es sein, dass das Skript in je nach Anforderung erweitert werden muss. Das Verhalten bei Verwendungen von Bedingten Texten wurde nicht getestet. Unterziehen Sie das Skript deshalb einem Test bevor Sie es auf eigenes Risiko einsetzen. Für Rückfragen zum Skript stehe ich gerne bereit. Lizenzbedingungen
veröffentlicht am 24 11 2011
Drucken
Mit diesen beiden Skripts ist es möglich über Tastaturkürzel den Inhalt eines Elements auszuwählen. Das erste Skript markiert den aktuellen Textknoten. Das Zweite markiert den gesamten Inhalt inklusive der inline Elemente.
Die Funktion steht hier zum download bereit. Das Skript einfach in das FrameMaker Verzeichnis ($FMHome\startup) kopieren und FrameMaker ggf. neu starten.
Shortcut: Esc+h+e+t
Textknoten auswählen
  1 . var doc = app.ActiveDoc;
  2 . var sel = doc.ElementSelection;
  3 . sel.beg.offset=0;
  4 . sel.end.offset=Constants.FV_OBJ_END_OFFSET;
  5 . var begLoc = doc.ElementLocToTextLoc (sel.beg);
  6 . var endLoc = doc.ElementLocToTextLoc (sel.end);
  7 . var textRange = new TextRange (begLoc, endLoc);
  8 . doc.TextSelection = textRange;
Shortcut: Esc+h+e+c
Elementinhalt auswählen
  1 . var doc = app.ActiveDoc;
  2 . var sel = doc.ElementSelection;  
  3 . var currentElement = null;
  4 . if (sel.beg.child.ObjectValid() && 
  5 .     sel.beg.child.ElementDef.ObjectValid() && 
  6 .     sel.beg.offset==0)
  7 .     currentElement = sel.beg.child;
  8 . else
  9 .     currentElement = sel.beg.parent;
 10 . var textRange = currentElement.TextRange;
 11 . textRange.beg.offset = textRange.beg.offset + 1 ;
 12 . doc.TextSelection = textRange;
Die Shortcuts können zu Beginn des Skripts beliebig geändert werden.
Lizenzbedingungen
veröffentlicht am 04 11 2011
Drucken
Will man eine Textdatei als Tabelle in FrameMaker importieren, wählt man als manuellen Weg das Menü Datei > Import und stellt entsprechende Parameter in den folgenden Dialog ein. Dies ist mittels ExtendScript auch in einem automatisierten Workflow möglich.
Probleme bereitet dies dann, wenn man eine Textdatei importieren möchte, in der die einzelnen Felder durch Tabulatoren getrennt sind. Der Tabulator liegt im Unicode-Zeichensatz auf dem Zeichencode 9 und kann nicht nur mit JavaScript mittels \t in einem String angegeben werden (siehe Zeile 1 im folgenden Snippet).
Allerdings führt diese Angabe als Wert für den Importparameter FS_CellSeparator (Zeile 11) zu keinem Erfolg. Die Lösung des Problems wirft uns in die Urzeiten von FrameMaker (vor der Version 8) zurück. Hier verwendete FrameMaker mit FrameRoman eine eigene Kodierung der Zeichen, und dort lag der Tabular auf dem Zeichencode 8. Sobal dieser Zeichencode übergeben wird (siehe Zeile 4), erhält man das gewünschte Ergebnis.
Textdatei importieren
  1 . var separator = "\t";
  2 . //use internal char enconding for tabs
  3 . if (separator=="" || separator=="\t")
  4 .   separator = 0x8;
  5 . 
  6 . var filename = "C:\\temp\\textfile.txt"
  7 . var doc = app.ActiveDoc
  8 . var importParams = GetImportDefaultParams();
  9 . var returnParams = new PropVals() ;
 10 . //... set other import parameters here
 11 . SetPropVal (Constants.FS_CellSeparator, separator, importParams);
 12 . doc.Import (doc.TextSelection.end , filename, importParams, returnParams)
 13 . 
 14 . function SetPropVal (prop, propval, params)
 15 . {
 16 .   var i = GetPropIndex(params, prop);
 17 .   if (i < 0) 
 18 .     return false ;
 19 .   if (typeof(propval) == 'number')
 20 .     params[i].propVal.ival  = propval;
 21 .   if (typeof(propval) == 'string')
 22 .     params[i].propVal.sval  = propval;
 23 .   return true;
 24 . }
Ich vermute, dass das Problem auch an anderen Stellen zum Vorschein kommen wird und auch noch andere Zeichen wie CarriageReturn und Linefeed betroffen sein werden.
Eine Gegenüberstellung der FrameRoman Kodierung zum ANSI-Zeichensatz findet sich im charater_sets.pdf(externer Link) ab Seite 6
veröffentlicht am 23 10 2011
Drucken
Aus der ersten Zelle einer im Dokument selektierten Reihe wird im folgenden Snipped der Text ermittelt und als Meldung an der Oberfläche ausgegeben.
GetText - UTF8
  1 . var row = app.ActiveDoc.SelectedTbl.TopRowSelection;
  2 . var cell = row.FirstCellInRow;
  3 . var textItems = cell.GetText (Constants.FTI_String) ;
  4 . var text = "" ;
  5 . for (var i = 0 ; i < textItems.length; i++)
  6 . {
  7 .   text += textItems[i].sdata ;
  8 . }
  9 . alert(text) ;
Enthält der Text Zeichen, die oberhalb des ASCII-Bereichs definiert sind (etwas Umlaute oder kyrillische Zeichen), werden diese Zeichen jetzt in derOberfläche UTF8-kodiert angezeigt. GetText liefert also in diesem Fall den internen FrameMaker Zeichencode zurück, wohingegen die Funktion AddText, mit der Text in die Zelle eingefügt werden kann, wie erwartet mit Unicode-Zeichenfolgen umgeht. Aus meiner Sicht also ein Bug, der nur umgangen werden kann, in dem die mit GetText() gelieferte Zeichenfolge dekodiert wird.
decode_utf8
  1 . function decode_utf8(utftext) 
  2 . {
  3 .   var plaintext = ""; 
  4 .   var i=0; 
  5 .   var c=c1=c2=0;
  6 .   while(i191) && (c<224)) 
  7 .     {
  8 .       c2 = utftext.charCodeAt(i+1);
  9 .       plaintext += 
 10 .              String.fromCharCode(((c&31)<<6) | 
 11 .              (c2&63));
 12 .       i+=2;
 13 .     }
 14 .     else 
 15 .     {
 16 .       c2 = utftext.charCodeAt(i+1); 
 17 .       c3 = utftext.charCodeAt(i+2);
 18 .       plaintext += 
 19 .              String.fromCharCode(((c&15)<<12) | 
 20 .               ((c2&63)<<6) | 
 21 .               (c3&63));
 22 .       i+=3;
 23 .     }
 24 .   }
 25 .   return plaintext;
 26 . }
Quelle: SelfHTML.de(externer Link)
Der Aufruf der Funktion ''decode_utf8" wird in Zeile 7 in das Beispiel integriert und die Meldung gibt den gewünschten Unicode-Text aus:
GetText - Unicode
  1 . var row = app.ActiveDoc.SelectedTbl.TopRowSelection;
  2 . var cell = row.FirstCellInRow;
  3 . var textItems = cell.GetText (Constants.FTI_String) ;
  4 . var text = "" ;
  5 . for (var i = 0 ; i < textItems.length; i++)
  6 . {
  7 .   text += decode_uft8(textItems[i].sdata) ;
  8 . }
  9 . alert(text)
veröffentlicht am 23 10 2011
Drucken
Ein nützliches Feature, welches in dieser Form mit dem FDK(externer Link) nicht zur Verfügung steht, ist die einfache Lokalisierbarkeit der Benutzeroberflächen mittels ExtendScript.
ExtendScript bedient sich hier der Funktion localize, welcher der sprachabhängige Oberflächentext als Array in JSON-Notation übergeben wird.
Das folgende Beispiel gibt im deutschen FrameMaker Hallo Welt! und beim Einsatz des Skripts in der englischen Variante Hello world! als Meldung aus.
localize
  1 . var m=localize({'de':"Hallo Welt!",'en':"Hello world!"});
  2 . alert(m);
Besonders hilfreich ist dies, wenn komplexe Oberflächen mittels ScriptUI entwickelt werden müssen.
veröffentlicht am 23 10 2011
Drucken
Mit ExtendScript oder besser mit JavaScript gibt es zwei Möglichkeiten durch eine Liste oder ein Array von Objekten zu iterieren
Snippet 1
  1 . var arr = new Array();
  2 . arr[0] = “Hello World!”;
  3 . for (var i =0; i < arr.length; i++)
  4 . {
  5 .   var item = arr[i];
  6 .   alert(item) ;
  7 . }
Snippet 2
  1 . var arr = new Array();
  2 . arr[0] = “Hello World!”;
  3 . for (var i in arr)
  4 . {
  5 .   var item = arr[i];
  6 .   alert(item) ;
  7 . }
Die etwas verkürzte Schreibweise hat bei der Entwicklung seinen Charme, weshalb ich diese bislang immer bevorzugt benutzt habe. Diese habe ich solange erfolgreich genutzt, bis die Schleife in einer Kundenumgebung keine Ergebnisse mehr lieferte.
Grund dafür war, dass der Kunde in seiner Umgebung die S1000D-Erweiterung(externer Link), die mit FrameMaker 10 erstmals als AddOn zur Verfügung steht, installiert hatte. Aus einem mir noch unbekannten Grund, deaktiviert die Installation der S1000D-Erweiterung diese Art der Iteration bzw. macht diese unmöglich. Auf die Ursache, wieso sich FrameMaker bzw. ExtendScript dann so verhält, habe ich noch nicht herausgefunden. Für Tipps bin ich deshalb dankbar.
Jedenfalls empfehle ich im Moment, gerade bei der Entwicklung von Skripts, die in unbekannten Systemumgebungen laufen, die erste Möglichkeit der Iteration. Dies Bedarf zugegebenermaßen einiger Disziplin.
veröffentlicht am 23 10 2011
Drucken
Mit dem Launch von Adobe FrameMaker 10(externer Link) im Januar 2011 steht für die Automatisierung des Editors mit ExtendScript eine weitere Programmierschnittstelle zur Verfügung. Bislang war es möglich FrameMaker mit Plugins, die auf Basis des FrameMaker Developer Kit (FDK)(externer Link) entwickelt wurden, oder mit der bekannten und etablierten Skriptsprache FrameScript(externer Link), immer wieder kehrende manuelle Arbeitsschritte zu automatisieren, um so das redaktionelle Leben für den Anwender zu erleichtern.
ExtendScript basiert auf der Skriptsprache JavaScript und erweitert diese Sprache um den Zugriff auf das FrameMaker interne Datenmodell. Neben Kenntnissen in JavaScript sind Kenntnisse über das Datenmodell und die Zugriffsfunktionen auf die jeweiligen Objekte Voraussetzung für die ExtendScript-Entwicklung.
Besonders hilfreich bei der Entwicklung von Oberflächen ist das in ExtendScript integrierte Framework ScriptUI. Wer bisher Plugins für FrameMaker mit der FDK entwickelt hat, wird dieses Feature sehr zu schätzen wissen. Da Produkte der Creative Suite(externer Link) schon seit längerem mit ExtendScript automatisiert werden können, besteht ein weiterer Vorteil in der BridgeTalk Komponente, die die Interaktion zwischen verschiedenen Adobe Produkten (z.B. Adobe Bridge, Adobe Photoshop, Adobe Illustrator, Adobe InDesign) ermöglichen. Dies war mit den bisherigen Programmierschnittstellen zwar auch möglich, bedurfte aber tieferer Kenntnisse in der Interprozesskommunikation.
Neben den offiziellen Blogs von Adobe(externer Link), finden Sie in meinem Blog zukünftig hilfreiche Hinweise und Snippets, die den Einstieg in das neue Skriptinginterface erleichtern sollen. Beginnen wir mit einigen hilfreichen Links.

Scripting Guides
Scripting Guide Adobe FrameMaker 10(externer Link)
Scripting Guide Adobe FrameMaker 10 (Online Help)(externer Link)
OMV-XML-Datei für den Object Model Viewer(externer Link)
JavaScript Tool Guide(externer Link)
Script UI Object Reference(externer Link)

Blogs / Foren
Debra Herman - Extending FrameMaker(externer Link)
Adobe Forum Extend Script(externer Link)
Adobe TCS Blog – ExtendScript of the Week(externer Link)

Script UI
Peter Kahrel - Script UI for dummies(externer Link)
Script UI Builder – Design Komponente für GUIs(externer Link)
Rapid Script UI – Design Komponente für GUIs(externer Link)
veröffentlicht am 23 10 2011
Drucken