コモノExtendScript100本ノック

超初心者のDTPオペレーターが週にひとつスクリプトを書くブログ

065.【Id】異なるinddの同名段落スタイルの差分を取得

※未完成
※同じ名前の段落スタイルであっても、スタイルグループが異なる場合は別物として扱う

挙動

前準備

  1. Github上でextendscript-es5-shimとして公開されているプロジェクトから、indexOf()isArray()のポリフィルをダウンロードする
  2. このスクリプトと同階層に「indexOf.js」「isArray.js」を移動する

実行

  1. ドキュメントを2つ開いた状態でスクリプトを実行する
  2. 各inddの同名同グループの段落スタイルを比較する
  3. プロパティの値が異なる場合、差分を取得する
  4. デスクトップにlog.txtを出力する

コード

//@include "./indexOf.js"
//@include "./isArray.js"

if (app.documents.length !== 2) {
    alert("2つのドキュメントを開いてください");
    exit();
}

var indd0 = app.documents[0];
var indd1 = app.documents[1];

//各inddの全段落スタイルの情報を取得
var pStyles0 = getPstylesInfo(indd0);
var pStyles1 = getPstylesInfo(indd1);

//両inddで名前とグループ名の一致する段落スタイルを取得
var dupe = [];
for (var i = 0; i < pStyles0.length; i++) {
    for (var j = 0; j < pStyles1.length; j++) {
        if (pStyles0[i]['name'] === pStyles1[j]['name'] && pStyles0[i]['group'] === pStyles1[j]['group']) {
            dupe.push([pStyles0[i], pStyles1[j]])
        }
    }
}

if (dupe.length === 0) {
    alert("重複したスタイルはありませんでした");
    exit();
}

var log = "スタイル名\tグループ名\tプロパティ\t" + indd0.name + "\t" + indd1.name;
for (var i = 1; i < dupe.length; i++) { //段落スタイルなしはスキップ
    var tmpStyle = indd0.paragraphStyles[0];
    log = log + "\n" + compareProp(tmpStyle, dupe[i][0]['obj'], dupe[i][1]['obj'])
}

writeTxt("~/Desktop/log.txt", log);

function compareProp(tgt, a, b) {
    var r = "";
    for (p in tgt) {
        var propn = p;
        var aprop = bprop = "";
        var skip = ["properties", "parent", "preferences", "styleUniqueId", "id","index"]; //このプロパティはスキップする
        if (skip.indexOf(p) === -1) {
            switch (getType(a[p])) {
                case "object": //nextStyleなどobjectの場合はnameで比較
//~                     compareProp(a[p], a[p], b[p]); //本当はここで再帰処理するべきなんだと思う
                    try {
                        if (a[p].name !== b[p].name) {
                            aprop = a[p].name.replace(/\t/g," ");
                            bprop = b[p].name.replace(/\t/g," ");
                        }
                    } catch (e) { //nameで比較できない場合は比較しない
                        aprop = "この項目は比較しませんでした"
                    }
                    break;
                case "array":
                    if (a[p].toSource() !== b[p].toSource()) {
                      if (typeof a[p][0] ==="object") { //tabListなどobjectを要素として持つ配列の場合
                        aprop = "この項目は比較しませんでした"
                        } else {
                        aprop = a[p];
                        bprop = b[p];
                        }
                    }
                    break;
                case "string":
                    if (a[p] !== b[p]) {
                        aprop = a[p].replace(/\t/g," ");
                        bprop = b[p].replace(/\t/g," ");
                    }
                break;
                default: //enum・数値・真偽値などの場合
                    if (a[p] !== b[p]) {
                        aprop = a[p];
                        bprop = b[p];
                    }
                    break;
            }
        }
        if (aprop !== "") {
            r = r + [dupe[i][0]['name'], dupe[i][0]['group'], p, aprop, bprop].join("\t") + "\n";
        }
    }
    return r
}

//全段落スタイルの情報を取得
function getPstylesInfo(tgtDoc) {
    var result = [];
    for (var i = 0; i < tgtDoc.allParagraphStyles.length; i++) {
        var tmp = tgtDoc.allParagraphStyles[i];
        result.push({
            name: tmp.name,
            group: getStyleGroup(tmp).replace(/\//, ""),
            obj: tmp
        });
    }
    return result;
}

//所属スタイルグループ名を取得
//入れ子になっている場合はスラッシュで繋ぐ
function getStyleGroup(tgt) {
    var tgtGroup = ""
    if (tgt.parent.constructor.name === "Document") {
        return "";
    }
    tgtGroup = getStyleGroup(tgt.parent) + "/" + tgt.parent.name;
    return tgtGroup;
}

//テキスト書き出し
function writeTxt(path, txt) {
    var fObj = new File(path);
    fObj.encoding = (/csv$/.test(path)) ? "Shift-JIS" : "UTF-8"; //CSVで書き出す場合はShift-JIS
    if (fObj.open("w")) {
        fObj.write(txt);
        fObj.close();
        return fObj;
    } else {
        alert("ファイルが開けません\n" + path);
    }
}

//型判定
//最初に大部分をふるい落とすほうがよい?
//if (typeof obj !== "object") {return typeof obj;}
function getType(obj) {
    if (obj === null){
      return "null";
      }
    if (Array.isArray(obj)) {
        return "array";
    }
    if (obj.constructor.name === "Enumerator") {
        return "enumerator";
    }
    return typeof obj;
}

メモ

分かったこと

  • Enumulatorについて
var tgt = app.activeDocument.paragraphStyles[2].justification;
$.writeln(typeof tgt);//object
$.writeln(Object.prototype.toString.call(tgt));//LEFT_JUSTIFIED
$.writeln(tgt.constructor.name);//Enumerator
$.writeln(tgt.toSource());//({})
var tgt2 = app.activeDocument.paragraphStyles[4].justification;
$.writeln(tgt===tgt2);//true enum同士で比較できる

積み残し

  • テストが不十分。おそらくバグがある。
  • オブジェクト同士の比較について別の方法を模索する。
    現状、値がオブジェクトの場合(「次の段落スタイル」など)はnameプロパティだけを比較している。
    そのため名前が同じで中身が違う場合でも同じと見なしてしまう。
  • 3つ以上のinddでも使えるようする。
    ブックドキュメントの同期をする前に差分をチェックしたいなと思ったのがきっかけなので、複数のinddを対象としたい。

参考