Help us understand the problem. What is going on with this article?

デザイン自動化(InDesign基本編)

InDesignをJavaScriptで制御する際に基本になる詰まりどころをまとめていきます。
※ちょっとずつ追記していく予定です。

開発環境(ExtendScript Toolkitを使います)

注意点。
起動後にソースコードを実行する対象アプリケーションを選択する必要があります。

JAVASCRIPTコンソールに文字を出力する。

プログラムの試行錯誤時にコンソールに変数情報を出力すると何かと便利です。

//改行なし
$.write("Hello World!!!!!");
//改行あり
$.writeln("Hello World!!!!!");

JAVASCRIPTコンソールの表示内容をクリアしたり、文字の大きさを変更するには右上のプルダウンを選択します。
下図の赤枠で囲った部分です。

スクリーンショット-2018-09-08-17.10.25.jpg

フォルダー操作の基本

フォルダー選択画面を表示してフォルダーを選ぶ

var folderObj = Folder.selectDialog ();
$.writeln(folderObj.fsName);

備考:InDesignで定義されているFileオブジェクトやFolderオブジェクトはフォルダーの区切り文字がWindowsとmacOS両方で/(スラッシュ)で統一されているようです(macOS 10.13 InDesignCS5、Windows10 InDesign CCで確認)。

サブフォルダ/サブファイルを取得する

var folderObj = Folder.selectDialog ();
var subFolderObjList = folderObj.getFiles();

for (var i = 0; i < subFolderObjList.length; i++) {

    $.writeln(subFolderObjList[i].fsName); 
}

実行結果
スクリーンショット 2018-09-08 22.36.14.png

テキストファイル処理

テキストファイルを読み込む

function main() {
    var text_content = "";

    f = new File("/Users/efla/Desktop/test/test.xml");
    if (fh = f.open("r")) {
            text_content = f.read();
            f.close();
    }

     $.writeln(text_content);
}

main();

実行結果
スクリーンショット 2018-09-29 19.52.32.png

XMLファイルの操作

XMLファイルの中身を取り出す。

色々な文法でXMLのパースができます。

ソースコードにXML構造をゴリゴリ書く。

function main() {
    var text_content = "";

    f = new File("/Users/efla/Desktop/test/test.xml");
    if (fh = f.open("r")) {
            text_content = f.read();
            f.close();
    }

     $.writeln(text_content);

     var xml_contents = new XML(text_content);

     for (i=0;i<xml_contents.Text.length();i++) {
         $.writeln(xml_contents.Text[i]);
     }
}

main();

実行結果
スクリーンショット 2018-09-29 20.05.34.png

XPathを使う

function main() {
    var text_content = "";

    f = new File("/Users/efla/Desktop/test/test.xml");
    if (fh = f.open("r")) {
            text_content = f.read();
            f.close();
    }

     $.writeln(text_content);

     var xml_contents = new XML(text_content);

     for (i=0;i<xml_contents.xpath ("/Text").length();i++) {
         $.writeln(xml_contents.xpath ("Text")[i]);
     }
}

main();

実行結果
スクリーンショット 2018-09-29 20.05.34.png

属性値を取り出す

メソッドattributeでノードの属性値を取得できます。

function main() {
    var text_content = "";

    f = new File("/Users/efla/Desktop/test/test.xml");
    if (fh = f.open("r")) {
            text_content = f.read();
            f.close();
    }

     $.writeln(text_content);

     var xml_contents = new XML(text_content);

     for (i=0;i<xml_contents.Text.length();i++) {
         $.writeln(xml_contents.Text[i]);
         $.writeln(xml_contents.Text[i].attribute("color"));
     }
}

main();

実行結果

スクリーンショット 2018-09-30 9.27.29.png

InDesignドキュメントとファイル操作

ファイパスを指定してInDesignファイルを開く

function main() {

   // ドキュメントを開く
   f = new File("/Users/efla/Desktop/test.indd");
   doc = app.open(f);

   $.writeln(doc);
}

main();

新規InDesignドキュメントを作成・保存・閉じる

function main() {

   // 新規ドキュメントを作成する
   doc = app.documents.add();

   $.writeln(doc);

   // 新規ドキュメントを保存する
   doc.save("/Users/efla/Desktop/hoge.indd");
   // 新規ドキュメントを閉じる
   doc.close();
}

main();

PDF出力

アクティブドキュメントをPDFファイルに書き出しする。

既定のプリセットで書き出す。

function main() {

    app.activeDocument.exportFile(ExportFormat.pdfType, "/Users/efla/Desktop/hoge.pdf", false);
 }

main();

プリセットを指定して書き出す。

function main() {

    presetName = "[PDF/X-1a:2001 (日本)]";
    app.activeDocument.exportFile(ExportFormat.pdfType, "/Users/efla/Desktop/hoge.pdf", false,presetName);
 }

main();

文字列操作

正規表現と完全一致するか調べる。

function main() {

    Str = 0987

    if (/^[0-9][0-9][0-9][0-9]$/.test(Str)) {
       alert(correct);
    }
 }

main();

画像操作

画像をドキュメントに配置する。

function main() {

   pageObj = app.activeDocument.pages[0];

   var imageObj = pageObj.textFrames.add();
   imageObj.visibleBounds = ["10mm","10mm","30mm","30mm"];
   imageObj.contentType = ContentType.graphicType;
   imageObj.place(new File("/Users/efla/Desktop/IMG_1453.png"));
   imageObj.fit(FitOptions.fillProportionally);
}

main();

結果
スクリーンショット 2018-10-20 19.12.55.png

画像のリンク情報を取得する

function main() {

   pageObj = app.activeDocument.pages[0];

   var imageObj = pageObj.textFrames.add();
   imageObj.visibleBounds = ["10mm","10mm","30mm","30mm"];
   imageObj.contentType = ContentType.graphicType;
   imageObj.place(new File("/Users/efla/Desktop/IMG_1453.png"));
   imageObj.fit(FitOptions.fillProportionally);

   var glist = imageObj.allGraphics; 

   for (i=0;i<glist.length;i++) {

          g = glist[i];
          l = g.itemLink;
          $.writeln(g.itemLink.name);   // リンク先のファイル名取得
          $.writeln(l.filePath);              // リンク先のファイルパスを取得
   }  
}

main();

結果
スクリーンショット 2018-10-21 7.06.31.png

画像のメタ情報・位置情報を取得する

function main() {

   pageObj = app.activeDocument.pages[0];
   var glist = pageObj.allGraphics; 

   for (i=0;i<glist.length;i++) {

          var g = glist[i];                              // Imageオブジェクト
          var l = g.itemLink;                         // Linkオブジェクト

          $.writeln(g);
          $.writeln(l);
          $.writeln(g.itemLink.name);       // リンク先のファイル名取得
          $.writeln(l.filePath);                  // リンク先のファイルパスを取得
          $.writeln(g.imageTypeName);    // リンクファイルの形式を取得

          $.writeln(g.absoluteHorizontalScale);
           $.writeln(g.absoluteVerticalScale);
          $.writeln(g.absoluteRotationAngle);
          $.writeln(g.absoluteShearAngle);

          $.writeln(g.horizontalScale);
          $.writeln(g.verticalScale);
          $.writeln(g.rotationAngle);
          $.writeln(g.shearAngle);

          $.writeln(g.visibleBounds);

          $.writeln(g.actualPpi);

          $.writeln(g.clippingPath);
   }  
}

main();

結果
スクリーンショット 2018-10-21 18.30.06.png

文字組処理

テキストフレームを作成

function addSwatch(c,m,y,k,name) {

   var i=0;

   for (i=0;i<app.activeDocument.colors.length;i++) {
        if (app.activeDocument.colors[i].name==name) {
            app.activeDocument.colors[i].remove();
        }
   }

   colorOption = {colorValue:[c,m,y,k],model:ColorModel.process,space:ColorSpace.cmyk,name:name}
   colorObj = app.activeDocument.colors.add(colorOption);    

   return colorObj;
 }

function main() {

    // 新しいテキストフレームを追加する。
    var tf = app.activeDocument.pages[0].textFrames.add();
    // テキストフレームの位置と大きさをページの左上を原点とする絶対座標で指定する。
    tf.visibleBounds = ["0mm","0mm","10mm","30mm"];
    // テキストフレームを移動させる(ページの左上を原点とする絶対座標)
    tf.move(["5mm","5mm"]);
    // テキストフレームを移動させる(今の位置から移動距離を指定)
    tf.move(undefined,["2mm","2mm"]);
    // テキストフレームに文字列を設定する。
    tf.contents = "みすずちんにはは";

    // 文字のQ数を指定する
    tf.paragraphs[0].characters[0].pointSize = "24Q";
    //テキストフレームを黒で塗りつぶす
    var s1 = addSwatch (0, 0, 0, 100, "");
    tf.fillColor = s1;
    // 文字の色とフォントを変更
    var s2 = addSwatch (0, 0, 0, 0, "");
    var s3 = addSwatch (100,0,0,0, "C100");
    for (i = 0; i < tf.paragraphs[0].characters.length;i++) {
       tf.paragraphs[0].characters[i].strokeColor = s2;
       tf.paragraphs[0].characters[i].fillColor = s3;
    }
    // 文字のフォントを変更
    var f = app.fonts.item("ヒラギノ丸ゴ Pro");
    tf.paragraphs[0].appliedFont = f;
}

main();

結果(画像は別記画像処理で追加)

スクリーンショット 2018-11-04 13.34.29.png

選択

スクリプトを作るときに、選択の解除や吟味をする処理を入れておかないと、トラブルの元になりやすい。

現在の選択をクリアする

function main() {

    app.select(NothingEnum.nothing);
 }

main();

全てのオブジェクトを選択する

function main() {

    // 必要に応じて、ロックオブジェクトやロックレイヤーの状態を調整する    

    app.select(SelectAll.all);   // 全てのオブジェクトを選択する
}

main();

現在の選択で、テキストフレーム以外の選択を解除する。

function main() {

    var sel = app.activeDocument.selection;
    for (i=0;i<sel.length;i++) {

       if ( sel[i].contentType!=ContentType.textType ) {

          sel[i].select(SelectionOptions.removeFrom);
       }
    }
 }

main();

コピー&ペースト

選択しているオブジェクトをコピーペーストする

function main() {

    try {
        app.copy();
        app.paste();
    } catch(e) {

     alert(e);
    }
 }

main();

結果(オブジェクトを選択している時)

スクリーンショット 2018-11-11 17.17.30.png

結果(オブジェクトを選択していない時)

スクリーンショット 2018-11-11 17.18.20.png

選択しているオブジェクトを同じ位置にペーストする

function main() {

    try {
        app.copy();
        app.pasteInPlace();
    } catch(e) {

     alert(e);
    }
 }

main();

結果

スクリーンショット 2018-11-11 17.32.51.png

レイヤー操作

レイヤー情報を取得する

function main () {

    var lList = app.activeDocument.layers;

    for (i = 0; i < lList.length;i++) {

        $.writeln(lList[i].name);    // レイヤー名
        $.writeln(lList[i].locked);   // レイヤーロック状態 
        $.writeln(lList[i].visible);   // レイヤー表示非表示状態
    }
}

main();

実行結果

スクリーンショット 2018-11-11 22.03.24.png

線を扱う

線に矢印があるかどうか確認する

function main() {

    var pageObj = app.activeDocument.pages[0];
    var total = pageObj.allPageItems.length;

    var i = 0;

    for (i=0;i<total;i++) {

       var pageItem;

       pageItem = pageObj.allPageItems[i];

       var typeName = pageItem.constructor.name;

        if (typeName=="GraphicLine") {

            $.writeln(typeName);
            $.writeln(pageItem.leftLineEnd.constructor.name);

            $.writeln(isDoubleHeadArrow (pageItem));
            $.writeln(isArrow(pageItem));

            //ArrowHead.NONE
            //ArrowHead.SIMPLE_ARROW_HEAD
            //ArrowHead.SIMPLE_WIDE_ARROW_HEAD
            //ArrowHead.TRIANGLE_ARROW_HEAD
            //ArrowHead.TRIANGLE_WIDE_ARROW_HEAD
            //ArrowHead.BARBED_ARROW_HEAD
            //ArrowHead.CURVED_ARROW_HEAD
            //ArrowHead.CIRCLE_ARROW_HEAD
            //ArrowHead.CIRCLE_SOLID_ARROW_HEAD
            //ArrowHead.SQUARE_ARROW_HEAD
            //ArrowHead.SQUARE_SOLID_ARROW_HEAD
            //ArrowHead.BAR_ARROW_HEAD
        }
    }
}

function isDoubleHeadArrow( item )  { 

    var typeName = item.constructor.name;

    if (typeName != "GraphicLine") {

        return false;1
   }

   if (item.leftLineEnd!=ArrowHead.NONE && item.rightLineEnd != ArrowHead.NONE) {

        return true;
   }

    return false;
}

function isArrow( item )  {

    var typeName = item.constructor.name;

    if (typeName != "GraphicLine") {

        return false;1
   }

   if (item.leftLineEnd != ArrowHead.NONE || item.rightLineEnd != ArrowHead.NONE) {

        return true;
   }

    return false;
}

main();

矢印の対比

スクリーンショット 2018-12-24 23.10.52.png

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away