Mrz 7
("foo" + + "bar") === "fooNaN" // Ergebnis: true

Auch als Programmierer kann man Sonntags Nachmittag Spaß haben :-/

http://wtfjs.com/

Ein schöne Sammlung merkwürdigen JavaScript Verhaltens, die auch in Adobes JS Implementation brav umgesetzt wurde. Nur bei einigen window und web-spezifischen Sachen dürfen wir nicht mitmachen.

Letztlich wird wieder mal deutlich, dass der Versuch Programmieren möglichst einfach zu machen zum scheitern verurteilt ist. Die meisten Sachen ergeben sich aus der automatischen Casting und Annahmen bzw. Vermutungen über die vermeintliche Aussage des Programmieres.


Mrz 1

Das Erstellen von Bildquellenverzeichnissen ist meist ein ungeliebter Teil im Produktionsprozess. Um diesen zu automatisieren habe ich ein kleines Skript geschrieben welches einem die Arbeit abnimmt.

Natürlich kann auch dieses Skript nicht erraten welches Bild von welchem Urheber kommt, dazu greife ich auf die XMP-Metadaten zurück. Dies müssen vorher natürlich in Photoshop, Bridge oder einem ähnlichen Porgramm korrekt eingetragen worden sein.

Der eigentliche Trick besteht darin die XMP-Daten des Link Objekts auszulesen:

_bildQuellen.push([_dok.links[i].linkXmp.author, getPageByObject(_dok.links[i])] ) 

Alle Autoren werden zusammen mit der Seitenzahl  im Array _bildQuellen gesammelt. Dieser Array muss dann mit der eigenen Sortierfunktion sortCreator() am Ende noch nach alfabetisch nach den Rechteinhaber sortiert werden.

Eine interessante Erweiterung ist es, die Bildpositionen auf den einzelnen Seiten zu erfassen. Dazu müsste man den Aufbau allerdings etwas abändern und seitenweise durch das Dokument gehen.

Hier kann man das ganze Skript und eine Beispieldatei mit Wikipedia Bildern herunterladen.


Feb 6

Ich mache an der Hochschule der Medien seit dem letzten Semester die Veranstaltung Technologisches Praktikum InDesign Satzautomation. Wesentlich Inhalte sind – wie könnte es anders sein – JavaScript für InDesign und Konzepte für den automatisieten Satz.

Die Unterlagen und Aufgaben liegen schon eine ganze Weile unbemerkt hier auf dem Server. Die Folien und Übungsaufgaben haben als Fokus Javascript mit InDesign und unterscheiden sich deswegen deutlich von den meisten mir bekannten webzentrierten JavaScript Einführungen.

2010-02-06_1653301. Termin

2. Termin

3. Termin

4. Termin

Leider fehlt der begleitende Text aus der Vorlesung. Aber für Leute die schon etwas programmieren können sicherlich eine hilfreiche Fundgrube.

Für Hinweise und Kommentare bin ich natürlich dankbar!

Viel Spaß beim Lernen!


Jul 20

Über mangelnde XML-Funktionen von InDesign wurde schon viel geschrieben. Praktisch ist das nachträgliche Erstellen von XML-Strukturen in einem klassisch aufgebauten InDesign-Dokument nur mit einfachsten Strukturen und Dokumenten möglich.

Über die Funktion Formate zu Tags zuweisen und die Tagging Voreinstellungen aus dem Kontext-Menu der Strukturpalette lassen sich Block-Level- und Inline-Elemente verknüpfen, Bilder und Tabellen werden zumindest ausgezeichnet.

einfache_story

Mit InDesign Bordmitteln lässt sich maximal das Folgende XML-Ergebnis erzielen:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Root>  <textrahmen> <u1>Überschrift 
</u1>  <p>Lorem ipsum <bold>dolor sit amet</bold>, consetetur ....</p>  </textrahmen> <textrahmen>  <legende><Figure href="file:///C:/Dokumente%20und%20Einstellungen/hp/Desktop/bild.png"/>
 Diese Bild zeigt das Logo des InDesign-Blogs </legende> </textrahmen>  <textrahmen>  <p>2tes Lorem ipsum dolor sit amet, consetetur ...</p>  </textrahmen> </Root>

Das aber auch nur, wenn das Bild als verankertes Objekt im Textrahmen der Legende platziert ist. In den meisten Fällen wird so allerdings nicht gearbeitet und Legende und Bild haben keinerlei Verknüpfung mehr. Die Anordnung der textrahmen-Elemente (Es handelt sich eigentlich um Stories, also auch verknüpfte Textrahmen) innerhalb der Strukur entspricht dem Erstellungszeitraum und nicht dem optischen Eindruck, was die Ergebnisse der meisten Exporte noch weitaus verwirrender gestaltet als das obige Beispiel.

Für ein produktives Konvertieren von alten Daten bietet sich ein Skripting-Workaround an. Als Konvetion wird festgelegt, das Legende und Abbildungspaar immer gruppiert sein müssen.

Das Skript-Snippet ist sehr einfach, für eine bessere Übersichtlichkeit verzichte ich auf das Handling der Gruppen und verarbeite nur die erste:

var _group = app.activeDocument.groups[0];
if (_group.allGraphics.length == 1 && _group.textFrames.length == 1) {
  var _xmlAbb = app.activeDocument.xmlElements[0].xmlElements.add("abbildung");
  // Falls die Grafik bereits verknüpft ist entfernen ich den Tag
  if (_group.allGraphics[0].associatedXMLElement != null) _group.allGraphics[0].associatedXMLElement.untag();
  _xmlAbb.xmlElements.add("bild", _group.allGraphics[0]);
  // Falls der Textrahmen bereits verknüpft ist entfernen ich den Tag
  if (_group.textFrames[0].associatedXMLElement != null) _group.textFrames[0].associatedXMLElement.untag();
  _xmlAbb.xmlElements.add("legende", _group.textFrames[0]);
}

Jede Gruppe die genau eine Abbildung und einen Textrahmen enthält wird damit  in die folgende Struktur, die eher einer sinnvollen XML-Struktur genügt,  gebracht:

<abbildung>
   <bild href="...bild.png"></bild>
   <legende>Diese Bild zeigt das Logo des InDesign-Blogs </legende>
</abbildung>

Eine Lösung für die Anordnung der Elmente in der richtigen Reihenfolge steht noch aus. Genauso ist es natürlich denkbar per Skript nah beeinander stehende Bild/Legende Objekte zu suchen und nicht den Umweg über die Gruppierung zu gehen.


Apr 18

InDesign rendered das Absatzattribut Abstand vor nur zwischen Textzeilen allerdings nicht in der ersten Zeile eines Textrahmens. Für einen automatischen Umbruch ist allerdings ein Feature notwendig, das bspw. abgesenkte Kapitelüberschriften die unterhalb des Satzspiegels beginnen ermöglicht. Textrahmen manuell anzupassen verbietet sich aus verschiedenen Gründen. Beim Attribut Abstand nach gibt es das gleiche Problem, allerdings fehlt hier die praktische Relevanz.

Ein möglicher Workaround ist es, die Schriftart der Überschrift auf einen größeren Wert zu stellen und dann wieder kleiner zu skalieren. Die größere Schriftgröße muss dann natürlich dem gewünschten Abstandswert entpsrechen was bei Prozentangaben nicht ganz trivial ist.

abstandvor

Im Bild ist die obere Überschrift in 12pt die untere in 30,125pt mit einem Skalierungsfaktor von 40% dargestellt. Zur Erzeugung des Screenshots wurden zwei Textrahmen übereinandergelegt.

Dieses Verfahren funktioniert erstaunlich gut, bei einem manuell eingestellten Zeilenabstand auch für mehrzeilige Überschriften. Wer etwas Rechenfaul ist dem kommt mein spacebefore-Skript zu gute. Eigentlich ziemlich straight-forward heruntergeschrieben:

  var abstandVor = 5; //in Millimetern
  var par = app.selection[0].paragraphs[0];
  //1mm == 2.834645669pt
  abstandVor = abstandVor * 2.834645669;
  var origGroesse = par.pointSize;
  // Schriftgröße neu setzen
  par.pointSize = origGroesse + abstandVor;
  // Scale berechnen und zuweisen
  var scale =  (origGroesse / (origGroesse + abstandVor )) * 100;
  par.verticalScale = scale;
  par.horizontalScale = scale;

Leider entspricht das noch nicht ganz dem gewünschten Ergebnis, weil die Schrift anhand der Versalhöhe am Textrahmen ausgerichtet wird. Dazu muss der Schrift nochmal ungefähr die Hälfte der Größe hinzugegeben werden.

  abstandVor = (abstandVor + (abstandVor/2.5)) * 2.834645669;

Der Wert 2.5 ist allerdings nur ein ungefährer Mittelwert, ein genauer Wert ist Abhängig von der Versalhöhe der Schrift. Auf diesen Wert hat man über InDesign keinen direkten Zugriff. Er ließe sich zwar theoretisch berechnen, wenn die Schrift temporär in Pfade umgewandelt würde. Bzw. in der ersten Zelle über den Abstand zwischen Grundlinie und Rahmenoberkante. Das überlasse ich aber mal jemanden anderen und gebe mich mit einem etwas ungenauen Ergebnis zufrieden.


Nov 29

Die guten alten Absatzlinien sind eine ziemlich geniale Möglichkeit Absätze mit Farbhinterlegungen oder Schmuckelementen zu versehen. Leider kann man sie nicht transparent setzen, so dass sie immer den Hintergrund vollständig verdecken.

Soll nun wie im Beispiel ein Hintgerundfoto mit Hilfe der Absatzlinien etwas zurückgenommen werden, benötigt man ein kleines Skript, dass die Hinterlegungen an den entsprechenden Stellen in das Dokument zeichnet. Bei Umbruchverschiebungen laufen sie nicht mit, dann muss das Skript erneut ausgeführt werden.

Die Idee ist die Absatzlinien selber zu erstellen. Dazu lege ich im voraus ein Absatzformat fest, bei welchem die Absatzlinien gezeichnet werden sollen.

const PSTYLE = "u2";

Am Anfang müssen eventuell noch vorhandene alten Hinterlegungen gelöscht werden, dazu versehe ich sie beim Erstellen (im zweiten Code Block bei rectangle.add) mit einem bestimmten label.

const MARKER = "%hpScriptObject%";
for (i = _dok.allPageItems.length -1; i > 0 ;i--) {
_dok.allPageItems[i].label == MARKER ? _dok.allPageItems[i].remove() : undefined ;
}

Nun durchsucht das Skript alle Absätze des Dokuments und errechnet die Größe der transparenten Hinterlegungen dynamisch.

// ... alle Stories, und Paragraphs einsammeln
if (_p.appliedParagraphStyle.name == PSTYLE) {
    for (j =0; j < _p.lines.length; j++) {
        var _x1 = _p.lines[j].characters[0].horizontalOffset;
        var _x2 = _p.lines[j].characters[-1].endHorizontalOffset;
        var _y2 = + offset + _p.lines[j].baseline ;
        var _y1;
        if (_p.lines[j].leading == Leading.AUTO ) {
            // Zeilenabstand selber berechnen 
            _y1 = _y2 +  _p.pointSize * (_p.autoLeading/100) *-1;
        }
        else {
            _y1 = _y2 +  _p.lines[j].leading * -1;
        }
        _dok.rectangles.add( undefined, LocationOptions.AFTER ,
                             _p.parentTextFrames[0] ,
                           { appliedObjectStyle:objStyle ,
                             geometricBounds:[_y1,_x1,_y2,_x2], label:MARKER } );
}

Interessant ist die in CS3 neu hinzugekommene Eigenschaft Character.endHorizontalOffset. Früher musste diese Position des letzten Buchstabens noch performanceraubend und mühsam errechnet werden.

Das vollständige Skript kann hier heruntergeladen werden. Zum Testen wird ein InDesign-Dokument mit den Absatzformat u2 benötigt – alternativ kann der Code natürlich auch direkt angepasst werden.


Nov 8

Zunächst einmal: Der Begriff Spationierung wird nicht einheitlich verwendet, laut Typolexikon und meiner Erfahrung wird damit sowohl das Verändern von Wortzwischenräumen (WZR) als auch die Schriftweitenveränderung bezeichnet.

Die Schriftweite/Tracking lässt sich mit ALT + PFEIL NACH LINKS/RECHTS verändern, allerdings ist darauf zu achten zunächst in den Voreinstellungen unter Einheiten, Kerning den Wert 1 einzutragen.

Im deutschen Schriftsatz ist das eigentlich nicht erwünscht – seit dem Einzug von DTP allerdings ziemlich weit verbreitet – dort werden stattdessen die Wortzwischenräume verändert. In InDesign auch kein Problem mit den ALT + SHIFT + STRG + J wird das Menu mit den Werten für den Blocksatz aufgerufen. Der Wert für das Optimun wird entgegen der Beschriftung auch für den Rau-/Flattersatz verwendet. Die Tastenkombination für die Veränderung ist etwas unbekannter, aber auch kein Problem: WZR vegrößern STRG + ALT + ß bzw. mit STRG + ALT + BACKSPACE wieder verringern. Allerdings ist der Wert meines Wissens nicht in der Benutzeroberfläche oder via Skripting auslesbar.

In beiden Fällen gilt: Ungeduldige drücken dazu die SHIFT Taste, das ganze kann mit STRG + ALT + Q wieder aufgelöst werden.

Soweit zu den Built-in-Features, aber hier gehts ja um praktische Verbesserungen. Das folgende Skript bringt eine Zeile unter minimaler Veränderung des Wortzwischenraums ein. Das Skript orientiert sich am Minimalwert der Blocksatzeinstellugen, die je nach typografischen Gefühl des Benutzers eingestellt sein sollten.

Das Ergebnis lässt sich über die Blocksatzeinstellungen. kontrollieren, wenn es nicht gefällt einfach einmal Rückgängig ausführen.

// Der Cursor muss in einem Absatz positioniert sein:
if (app.selection[0].constructor.name == "InsertionPoint") {
    var par = app.selection[0].paragraphs[0];
    var lines = par.lines.length;
    var oldDesiredWordSpacing = par.desiredWordSpacing;
    var cDesiredWordSpacing = par.desiredWordSpacing;

    // WordSpacing solange verringern bis eine Zeile weniger
    while (par.lines.length == lines) {
        // Das etwas unübersichtliche Konzpet erlaubt danach ein
        // einmaliges STRG + Z für das Rückgängig
        cDesiredWordSpacing--;
        par.desiredWordSpacing = cDesiredWordSpacing;
        if (par.lines.length == lines) {
            par.desiredWordSpacing = oldDesiredWordSpacing;
        }
        // Das Skript orientiert sich am minimumWordSpacing
        // Wert aus den Blocksatzeinstellungen.
        // Falls der Minimalwert unterschritten wird,
        // wird der Originalwert wieder hergestellt.
        if (par.desiredWordSpacing == par.minimumWordSpacing)  {
            par.desiredWordSpacing = oldDesiredWordSpacing;
            exit(1);
        }
    }
    exit(0);
}

Eine interessante Möglichkeit wäre es, eine Textbereich oder Textrahmen auszuwählen, und dort das Skript den Absatz finden zu lassen der den geringsten Eingriff benötigt.

Die Idee für das Skript basiert auf einer Forumsdiskussion. Das Ergebnis dort war ein Skript von Teus de Jong welches die Spationieurng verringert. Im Download habe ich ein Skript mit beiden Optionen.


Okt 4

Eine meiner Lieblingsneuerungen bei InDesign CS3 war die Möglichkeit mit Hilfe von Regulären Ausdrücken Suche/Ersetze-Vorgänge durchzuführen. Hier soll es aber nicht um die erweiterten Möglichkeiten der GREP-Funktion gehen.
Oftmals egibt sich in der Praxis die Anforderung, zunächst bestimmte Stellen in einem Dokument zu finden, diese dann allerdings nicht einfach zu ersetzen, sondern vorher nach bestimmten Regeln zu bearbeiten.
Dazu habe ich ein recht übersichtliches FindAndDo-Script geschrieben, in dem dann nur noch die Suchbedingung und die Verarbeitungsanweisung eingetragen werden muss.
Ein nicht zu vernachlässigender Punkt ist die rückwärts laufende For-Schleife: Wenn auf der Trefferliste Ersetzungen vorgenommen werden die eine veränderte Anzahl an Zeichen aufweisen, ändert sich der Index des Elternelements auf dem gesucht wurde. Die Folge ist, dass die darauf folgenden Ersetzungen an falschen Stellen ausgeführt werden (dem ursprünglichen Index).
Vielleicht mal das letzte Beispiel aus der Praxis damit es klarer wird: Alle Seitenverweise in einem Dokument waren vom Verlag per Hand aufgelöst worden, nun verschob sich der Umbruch ab Seite 106 um vier Seiten. Mit der normalen Suchfunktion hätte das bedeutet, alle Seitenverweise von Hand durchzugehen und je nach Postion zu verädern oder zu ignorieren. Bei 500 Verweisen wäre das ganze recht zeitaufwändig geworden, das Skript sieht dann so aus:

    // Zunächst alte Suchoptionen löschen, ich verwende hier die Grep Suche
    app.changeGrepPreferences = NothingEnum.nothing;
    app.findGrepPreferences = NothingEnum.nothing;
    // Nach Seitenverweisen des Typs (S. 99) suchen
    app.findGrepPreferences.findWhat = "\\(S..([0-9]+?)\\)";
    var erg = app.activeDocument.findGrep ();	

    for (var i = erg.length - 1; i >= 0; i--) {
        // Die Seitenzahl extrahieren
        var string = erg[i].contents.match (/[0-9]+/) ;
        // Wenn die Seitenzahl größer 106 ist wird sie erhöht
        if (parseInt(string) >= 106 ) {
            string = (parseInt(string) + 4);
        }
        erg[i].contents = "(S. " +  string + ")";
    }

PS: Die Seitenauflösung von Hand wäre auch überflüssig gewesen, wenn der Workflow von Anfang an sinnvoll geplant gewesen wäre …
PPS: Bei der Übergabe von Grep Suchanfragen müssen die Javascript String Konventionen beachtet werden, d.h. im besonderen die Forward-Slashes und Quotes müssen mit dem Forward-Slashe escaped werden (z.B. //). Ich empfehle im DEBUG Modus die Übergabe an das Suchfeld zu testen.