スクリプトでパステキストを扱う

Illustrator上で入力した文字(テキストオブジェクト)は以下の3種類のいずれかになります。

ポイントテキスト
エリアテキスト
パステキスト

f:id:shspage:20170514195339p:plain

先日、パステキストのパスを初めてJavaScriptで扱ってみたら 色々と発見などがあったので、記事にしたいと思います。

パステキストの判別

JavaScript でテキストオブジェクト(TextFrameItem)の種類を判別するには kind というプロパティを参照します。kind の値は上記の種別と対応した以下の3種類です。

TextType.POINTTEXT
TextType.AREATEXT
TextType.PATHTEXT

判別の処理は例えば以下のように行います。

// 選択オブジェクトがパステキストのときにメッセージを表示する。
var obj = app.activeDocument.selection[0];

if(obj.typename == "TextFrame"
   && obj.kind == TextType.PATHTEXT){
    // 処理
    alert("パステキストです");
}

パス上文字の始点と終点を取得する

パステキストで文字が沿っているパスの情報は、TextFrameItem の textPath というプロパティで取得できます。この値は TextPath オブジェクトです。

また、パス上での文字の始点・終点は、TextFrameItem の startTValue, endTValue から取得できます。

// 選択されているパステキストのstartTValue, endTValueを表示する。
var textFrame = app.activeDocument.selection[0];

alert("startTValue = " + textFrame.startTValue
      +"\rendTValue   = " + textFrame.endTValue);

f:id:shspage:20170514195537p:plain

~TValue( T の値 )という名前から想像できるように、この値はベジエ曲線のパラメーターに基づくものです。 *1

ベジエ曲線のパラメーターは2つのアンカー間の曲線上の位置によって 0~1 の値をとりますが、 ~TValue はこれに始点側アンカーのインデックスを足した値になっています。

例えば、パス上文字の始点がパスの1つ目と2つ目のアンカー の間にある場合、startTValueは 0~1 の間の値になり、 2つ目と3つ目のアンカーの間にある場合は 1~2 の間の値になります。 *2

…というのはオープンパスの場合で、 クローズパスの場合はもう少し考慮が必要な点があります。

クローズパスの場合

f:id:shspage:20170514195559p:plain

まず、上の図ではパスのアンカーは4つなので、 上記の考え方からすると startTValue と endTValue は 0~4 の間の値になりそうなんですが、 ここでの endTValue は 4.5 という値になっています。

文字がパスの始点(=終点)をまたいでいるので、 こうなるのだろうなという感じではありますが、 さらにこの文字の始点・終点を手作業で動かすと、 下の画像のようなおかしな値になります。

f:id:shspage:20170514195609p:plain

内部でどういう計算をしているのかわかりませんが、 この値から最初の画像のような素直な値を 取得するには、アンカーの数で割った余りを取得すればよいようです。 (次項の「注意が必要な点」も考慮してください。)

f:id:shspage:20170514195617p:plain

パス上文字の始点と終点を設定する

startTValue, endTValue は、適当な値を代入することで変更できます。

startTValue に 0、 endTValue に「アンカー数マイナス1」を設定すると、 パス上文字の範囲がパスの始点と終点になります。

注意が必要な点は、endTValue に startTValue より小さい値を設定するとエラー になることです。

前項のクローズパスでの性質を考慮して パステキスト (クローズパス)に startTValue, endTValue を設定する処理を function にすると以下のように書くことができます。

function setTValue(textFrame, startT, endT){
    var len = textFrame.textPath.pathPoints.length;
    startT %= len;
    endT   %= len;
    if(endT < startT){
       endT += len;
    }
    textFrame.startTValue = startT;
    textFrame.endTValue = endT;
}

スクリプトによるパステキストのパスの変更

TextPath は通常のパスと同じように pathPoints などの属性を持っているため、 スクリプトでアンカーやハンドルの位置や数を変えることができます。 (PathItem とはプロパティやメソッドが異なる部分もあります。)

スクリプトで TextPath の pathPoints を操作する場合に注意が必要な点は、 startTValue, endTValue の値が変わってくれないことです。 このため、処理によってアンカー数が変わる場合は、 これらの値の変更も必要になることがあります。

別に掲載 している「 円弧にする 」というスクリプトの中にこの位置の調整処理があります。 このスクリプトには下の画像のようにパステキストが沿うパスを 適当な円弧にする機能がありますが、 TextPath のアンカー数を変更し、パスの長さも変わるので、 文字の位置を再設定する必要があるのです。

f:id:shspage:20170514195649p:plain

処理はベジエ曲線に関する汎用的な操作(計算)の組み合わせです。 詳しくはここで書くと長くなるので、追って記事を作ろうと思いますが、 工程だけ順を追って書くと以下のようなことをしています。

1. パスの長さ ( L1 ) を算出する。
2. パスの始点から startTValue にあたるパス上の点までの長さ ( S1 ) を算出する。
3. L1 に対する S1 の割合 ( R ) を算出する。
4. パスを変更する。
5. 変更後のパスの長さ ( L2 ) を算出する。
6. L2 に割合 R を掛けた長さ ( S2 ) を算出する。
7. パスの始点からの長さが S2 になるパス上の点のパラメーターを算出する。
  パラメーターとアンカーのインデックスから startTValue が求められる。
8. startTValue を設定する。
(endTValue についても同様に行う。)

パスの長さを計算しているのは、TextPath に length というプロパティがないためです。

undoの問題

スクリプトで startTValue, endTValue を変更した場合、 スクリプト実行後に undo をしてもこれらが実行前の値に戻らないようです。

スクリプトでの変更は undo の対象になっていないのか、 内部的な仕組みはよくわかりませんが、

これについては今のところ以下のような手順で処理をして回避しています。

1. 元のパスを複製
2. 複製したパスを操作
3. 元のパスを削除

こうすると undo はパス自体の変更ではなく削除の undo になるので、 文字の位置は実行前の状態に戻ります。

文字を添わせる側を操作する

パステキストは手作業での操作により文字をパスの反対側に添わせることができますが、 このとき TextPath の polarity というプロパティの値が PolarityValues.NEGATIVE になります。 polarity の既定値は PolarityValues.POSITIVE です。

polarity はスクリプトで設定することもできます。

// パステキスト(文字は既定の側に沿う)を選択して実行すると、
// 文字がパスの反対側に沿う。
var textFrame = app.activeDocument.selection[0];
textFrame.textPath.polarity = PolarityValues.NEGATIVE;

f:id:shspage:20170514195813p:plain

polarity が NEGATIVE になると、パスの向き(PathPoint の順番)が逆になります。 このとき、startTValue と endTValue は元のパスの終点を 0 とした値となります。

とりあえず以上です

*1:ベジエ曲線のパラメーターについては追って記事にする予定です。

*2:1つ目のアンカーのインデックスが 0 になります。