Linienhintergrund automatisch erstellen

Wenn Sie Ihre Grafiken mit FrameMaker-Werkzeugen beschriften, werden Sie den Wunsch haben, die schwarzen Beschriftungslinien mit einer hellen Kontrastfarbe zu hinterlegen, damit dies auch auf dunklem Untergrund sichtbar bleibt. Zum Beispiel könnten Sie eine 1pt starke schwarze Linie mit einer 3pt weißen Linie hinterlegen wollen.

Voilá, das ist eine Aufgabe, die ich einmal mit **FrameScript** und **ExtendScript** angehen möchte, auch um die Gemeinsamkeiten und Unterschiede deutlich werden zu lassen. Wenn es Ihnen konkret hilft, fein!

Es stellt sich heraus, dass diese Teilaufgabe – wie vermutlich die meisten Aufgaben, die sich nur mit FrameMaker-Dokumenten beschäftigen – prinzipiell identisch gelöst wird. Ich gehe wie folgt vor:

0. Prüfe die Ausgangssituation: Ein *AFrame* (verankerter Rahmen) oder eine Grafik darin muss ausgewählt sein.
0. Entferne alle Linien, die 3pt und weiß sind, um bei wiederholtem Einsatz des Skripts nicht immer mehr Linien zu erzeugen.
0. Suche nach allen Linien die 1pt und schwarz sind, hinter diese platziere eine 3pt weiße Linie.

In den Zeilen 9 und 12 der Skripte sehen Sie die Zeilen, in denen diese Vorgaben angepasst werden können.

### FrameScript

~~~~
01 Run CreateBackgroundLines pvDoc(ActiveDoc);
02
03 Sub CreateBackgroundLines pvDoc
04 Local lvAFrame lvSrcProps lvTgtProps lvProps;
05 Local lvObj lvNextObj lvNewObj lvSrcColor lvTgtColor;
06
07
08 // fixed List of Properties: .BorderWidth, name of color
09 Set lvSrcProps = EUtl.StringList{1pt, ‚Black‘};
10
11 // fixed List of Properties: .BorderWidth, name of color
12 Set lvTgtProps = EUtl.StringList{3pt, ‚White‘};
13
14 If (pvDoc)
15 Set lvAFrame = GetAFrame{pvDoc};
16 If (not lvAFrame)
17 MsgBox ‚Select AFrame or graphic in AFrame. ‚ Mode(Note);
18 LeaveScript;
19 EndIf
20 If (lvAFrame)
21 // Get colors
22 Get Object DocObject(pvDoc) Type(Color) Name(lvSrcProps[2])
23 NewVar(lvSrcColor);
24 If (not lvSrcColor)
25 MsgBox ‚Color „‚ + lvSrcProps[2] + ‚“ not available.‘ Mode(Note);
26 LeaveScript;
27 EndIf
28 Get Object DocObject(pvDoc) Type(Color) Name(lvTgtProps[2])
29 NewVar(lvTgtColor);
30 If (not lvTgtColor)
31 MsgBox ‚Color „‚ + lvTgtProps[2] + ‚“ not available.‘ Mode(Note);
32 LeaveScript;
33 EndIf
34
35 Set Session.Displaying = False;
36 // delete existing with tgt properties
37 Set lvObj = lvAFrame.FirstGraphicInFrame;
38 Loop While(lvObj)
39 Set lvNextObj = lvObj.NextGraphicInFrame;
40 If (lvObj.ObjectName = ‚Line‘)
41 If (lvObj.BorderWidth = lvTgtProps[1]
42 and lvObj.Color.Name = lvTgtProps[2])
43 // hit!
44 Delete Object(lvObj);
45 EndIf
46 EndIf
47 Set lvObj = lvNextObj;
48 EndLoop
49
50 // find existing with src properties
51 Set lvObj = lvAFrame.FirstGraphicInFrame;
52 Loop While(lvObj)
53 Set lvNextObj = lvObj.NextGraphicInFrame;
54 If (lvObj.ObjectName = ‚Line‘)
55 If (lvObj.BorderWidth = lvSrcProps[1]
56 and lvObj.Color.Name = lvSrcProps[2])
57 // hit!
58 Set lvProps = lvObj.Properties;
59 New Line ParentObject(lvAFrame) NewVar(lvNewObj);
60 Set lvNewObj.Properties = lvProps;
61 Set lvNewObj.BorderWidth = lvTgtProps[1];
62 Set lvNewObj.Color = lvTgtColor;
63 Set lvNewObj.NextGraphicInFrame = lvObj;
64 EndIf
65 EndIf
66 Set lvObj = lvNextObj;
67 EndLoop
68 Set Session.Displaying = True;
69 Update DocObject(pvDoc) ReDisplay;
70
71 EndIf
72 EndIf
73 EndSub
74
75 Function GetAFrame pvDoc
76 Local lvObj;
77
78 Set Result = False;
79 If (not pvDoc)
80 LeaveSub;
81 EndIf
82 Set lvObj = pvDoc.FirstSelectedGraphicInDoc;
83 If (lvObj)
84 Loop While(lvObj.ObjectName != ‚AFrame‘)
85 Set lvObj = lvObj.FrameParent;
86 EndLoop
87 If (lvObj.ObjectName = ‚AFrame‘)
88 Set Result = lvObj;
89 EndIf
90 EndIf
91
92 EndFunction
~~~~

###ExtendScript

Es bedurfte nur minimaler Maßnahmen, um eine zeilengleiche Fassung mit ExtendScript zu erstellen:

~~~~
01 CreateBackgroundLines (app.ActiveDoc);
02
03 function CreateBackgroundLines (pvDoc) {
04 var lvAFrame, lvSrcProps, lvTgtProps, lvProps;
05 var lvObj, lvNextObj, lvNewObj, lvSrcColor, lvTgtColor;
06 var pt = 65536;
07
08 // fixed List of Properties: .BorderWidth, name of color
09 lvSrcProps = new Array(null, 1 * pt, ‚Schwarz‘);
10
11 // fixed List of Properties: .BorderWidth, name of color
12 lvTgtProps = new Array(null, 3 * pt, ‚Weiß‘);
13
14 if (pvDoc.ObjectValid()) {
15 lvAFrame = GetAFrame(pvDoc);
16 if (!lvAFrame) {
17 alert(‚Select AFrame or any graphic in AFrame. ‚);
18 return false;
19 }
20 if (lvAFrame.ObjectValid()) {
21 // Get colors
22 lvSrcColor = pvDoc.GetNamedColor(lvSrcProps[2]);
23
24 if (!lvSrcColor.ObjectValid()) {
25 alert(‚Color „‚ + lvSrcProps[2] + ‚“ not available in document.‘);
26 return false;
27 }
28 lvTgtColor = pvDoc.GetNamedColor(lvTgtProps[2]);
29
30 if (!lvTgtColor.ObjectValid()) {
31 alert(‚Color „‚ + lvTgtProps[2] + ‚“ not available in document.‘);
32 return false;
33 }
34
35 app.Displaying = false;
36 // delete existing with tgt properties
37 lvObj = lvAFrame.FirstGraphicInFrame;
38 while(lvObj.ObjectValid()) {
39 lvNextObj = lvObj.NextGraphicInFrame;
40 if (lvObj.type == Constants.FO_Line) {
41 if (lvObj.BorderWidth == lvTgtProps[1]
42 && lvObj.Color.Name == lvTgtProps[2]) {
43 // hit!
44 lvObj.Delete();
45 }
46 }
47 lvObj = lvNextObj;
48 }
49
50 // find existing with src properties
51 lvObj = lvAFrame.FirstGraphicInFrame;
52 while(lvObj.ObjectValid()) {
53 lvNextObj = lvObj.NextGraphicInFrame;
54 if (lvObj.type == Constants.FO_Line) {
55 if (lvObj.BorderWidth == lvSrcProps[1]
56 && lvObj.Color.Name == lvSrcProps[2]) {
57 // hit!
58 lvProps = lvObj.GetProps();
59 lvNewObj = pvDoc.NewLine(lvAFrame);
60 lvNewObj.SetProps(lvProps);
61 lvNewObj.BorderWidth = lvTgtProps[1];
62 lvNewObj.Color = lvTgtColor;
63 lvNewObj.NextGraphicInFrame = lvObj;
64 }
65 }
66 lvObj = lvNextObj;
67 }
68 app.Displaying = true;
69 pvDoc.Redisplay();
70 return true;
71 }
72 }
73 }
74
75 function GetAFrame (pvDoc) {
76 var lvObj, Result;
77
78 Result = false;
79 if (!pvDoc.ObjectValid()) {
80 return Result;
81 }
82 lvObj = pvDoc.FirstSelectedGraphicInDoc;
83 if (lvObj.ObjectValid()) {
84 while(lvObj.type != Constants.FO_AFrame) {
85 lvObj = lvObj.FrameParent;
86 }
87 if (lvObj.type == Constants.FO_AFrame) {
88 Result = lvObj;
89 }
90 }
91 return Result;
92 }
~~~~

### Unterschiede

Es gibt natürlich einige generell Unterschiede in der Syntax bei **FrameScript** (FS) und **ExtendScript** (ES), wie zum Beispiel die Funktionsaufrufe, die if-then-else-Verzweigungen und loop-while-Schleifen, auf die ich nicht eingehen möchte. Auch erfordert JavaScript und folglich ES Disziplin bei der Großkleinschreibung, FS erlaubt beliebige Schreibweisen von Kommandos, Eigenschaften und Variablenbezeichnern. Viel interessanter sind die feinen Unterschiede beim Zugriff auf FrameMaker.

* Z. 1: Der Zugriff auf das Session-Objekt ist bei FS implizit, bei ES muss *app.* vorangestellt werden.
* Z. 6: Bei Maßangaben rechnet ES mit 1/65636 eines Punkts, also müssen Angaben in pt immer mit diesem Wert multipliziert werden.
* Z. 9/12: Um später den gleichen Index beim Zugriff auf die Listen verwenden zu können, ES aber die Indizierung immer bei 0 beginnt, habe ich hier einen an sich überflüssigen Eintrag *null* hinzugefügt.
* Z. 14: Während bei FS ungültige Objektreferenzen in If-Abfragen automatisch als *false* bewertet werden, muss bei ES immer noch *.ObjectValid()* angehängt werden.
* Z. 22/28: Die ES-Methoden *.GetNamedXXX* sind eleganter als in FS das Kommando *Get Object Type(XXX)*, zudem ist dies auch eine Abkürzung gegenüber dem FDK.
* Z. 40/54: Um den Typ eines Objekts zu ermitteln, kann ich bei FS die Eigenschaft *.ObjectName* für einen String-Vergleich verwenden, bei ES vergleiche ich *.type* mit einer vordefinierten Konstanten; ich könnte statt *Constants.FO_Line* auch *17* angeben, das würde die Lesbarkeit des Skripts aber erschweren.
* Z. 58/60: Statt einer einfache Zuweisung geschieht der Zugriff auf die EIgenschaften eines Objekts bei ES mit den Methoden *.GetProps()* und *.SetProps(props)*.

Bei der vorliegenden Aufgabenstellung halten sich die Unterschiede also in Grenzen. Und das ist durchaus erfreulich, wenn da nicht ein eklatanter Mangel an Dokumentation für *ExtendScript* wäre…

### Lizenz und Download

Beide Fassungen des Skripts finden Sie in der Datei CreateBackgroundLines.zip.

Selbstverständlich gibt es eine Vielzahl von möglichen Optimierungen, zum Beispiel ist das Verhalten bei Linien mit Pfeilspitzen suboptimal… machen Sie mit?

Dieser Beitrag wurde unter Lernen, Lernen veröffentlicht. Setze ein Lesezeichen auf den Permalink.

2 Responses to Linienhintergrund automatisch erstellen

  1. Hallo Hr. Müller-Hillebrand!
    Soweit ok, aber ich habe grundsätzlich das Bedürfnis, blaue, schwarze etc. Farben von Linien mit einer hellen Seele zu versehen. Hierbei müssen auch Linienenden als Pfeil ausgebildet werden können.
    Mit freundlichem Gruß
    Ulrich König

  2. Es ist spannend, wie unterschiedlich die Anwendungsfälle sind. Einmal soll ein kontrastierender Hintergrund erstellt werden, ein andermal eine helle »Seele«. Die Regeln müssen eindeutig sind, dann steht einer weitergehenden, individuell abgestimmten Automatisierung nichts im Wege; ich erstelle dafür gerne ein Angebot 🙂

Kommentare sind geschlossen.