Illustratorに慣れていない方の作ったデータを見ると、大抵ほぼすべてのテキストが左揃えになっています。これだと文字が増えたり、大きさが変わったりするたびに位置を直さないといけないケースが多いですね。
しっかり者の皆さんは、きっと毎日毎日適切な行揃えに直す日々を送っていることでしょう。お疲れさまです。
幸いにもこの問題にはすでにたくさんの解決策が公開されています。
- Illustrator:文字の位置そのままで揃え方向を変えたい時に使うやつ|車車車く本牛勿 -Rollin’ Real-
- 細かすぎて伝わらないIllustratorの小技(番外編)デフォルトではできないこと|DTP Transit
- 【無料】文字揃えを変えても位置はそのままなスクリプトをどうぞ!(Illustrator)|GOROLIB DESIGN – はやさはちから –
ただどれも一長一短だったので、今回は以下のような私のおすすめ仕様で書いたJavaScriptを紹介します。
- 左揃え(上揃え)・中央揃え・右揃え(下揃え)がそれぞれ別のスクリプトになっていて、個別にショートカットを割り当てられる
- 左揃えにできる
- 文字サイズのオーバーライドなど、副作用が発生しない
目次
左揃えにできるとか、副作用って何?
実はIllustrator CS4以降には、スクリプトで左揃えを指定しても無視されるというバグ(仕様?)があります。より正確に言うと、適用されている段落スタイルの初期値と同じ行揃えをスクリプトで指定しても、それは無効になってしまいます。
大抵のテキストは[初期設定段落スタイル]が適用されていて、それは左揃えが初期設定です。つまり普通に書いたスクリプトでは左揃えにできません。
これを解決する策の1つでは、過程内で「文字をリサイズして元に戻す」という動作をします。しかしそこで文字サイズ・垂直比率・水平比率などがオーバーライドされるという副作用が発生します。
仮にオーバーライドされても、世の中の大半の方には影響ありません。ただし私のように文字スタイル・段落スタイルできっちり管理している場合は、オーバーライドが運用の邪魔になります。
今回紹介するスクリプトは、バージョンを問わず行揃えを普通に適用できます。どれも副作用はなく、変わるのは行揃えだけです。
changeJustificationバージョン1では左揃えにできるIllustratorに制約がありましたが、バージョン2ではCS3以降ならどれでもセット可能な見込みです。よりシンプルな処理になったため、対応Illustratorが広くなりました。
今回のスクリプトchangeJustificationは9個セットです。
| 種類 | 動作説明 |
|---|---|
| justification=left.jsx | 左 / 上揃えを適用する |
| justification=center.jsx | 中央揃えを適用する |
| justification=right.jsx | 右 / 下揃えを適用する |
| justification=fullJustifyLastLineLeft.jsx | 均等配置 (最終行左 / 上揃え) を適用する |
| justification=fullJustifyLastLineCenter.jsx | 均等配置 (最終行中央揃え) を適用する |
| justification=fullJustifyLastLineRight.jsx | 均等配置 (最終行右 / 下揃え) を適用する |
| justification=fullJustify.jsx | 両端揃えを適用する |
| justification=dialog.jsx | ダイアログにて行揃えを指定して適用する |
| justification=argv.jsx | スクリプト実行引数にて行揃えを指定して適用する |
使いかた
こちらのファイルをダウンロードしてください。
使いかたは、テキストフレームを選択してスクリプトを実行するだけ。まるで手が勝手に仕事をしているようです!

応用
justification=dialog.jsx では、ダイアログで行揃えを選択して実行できます。各項目の左にある数字がショートカットキーです。単体でキーを押して項目を選択し、returnキーまたはOKボタンで確定します。
justification=argv.jsx は、Keyboard Maestroとスクリプトファイルを実行するKeyboard Maestro用アクション(無料)とのセットで使います。argumentsに入力する文字を変えれば使うファイルをこれ1つに絞れるので、スクリプトをアップデートするときなどに管理が楽になります。

作者に感謝を伝えたい!
Buy me a coffeeは、クレジットカード払いなどでクリエイターにコーヒーをおごれるサービスです。ツール・情報が役に立った! 感謝の気持ちを表現したい! というかた、おごっていただけましたら嬉しいです☕️
これでまた少し仕事が速くなりました。今日もさっさと仕事を切り上げて好きなことをしましょう!
コードはこちら
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
/** * @file 選択したテキストフレームの位置を変えずに行揃えを変更する。縦組み/横組みは問わない * @version 2.0.0 * @author sttk3.com * @copyright © 2025 sttk3.com */ //#target 'illustrator' //@targetengine 'com.sttk3.changeJustification' (function(argv) { $.localize = true ; if(app.documents.length <= 0) {return ;} var doc = app.documents[0] ; var sel = allPageItemOfSelection(doc.selection) ; if(sel.length <= 0) {return ;} var targetItems = filterItems(sel, function(item) { return /^TextFrame$/.test(item.constructor.name) ; }) ; var targetItemLength = targetItems.length ; if(targetItemLength <= 0) {return ;} var aiVersion = appVersion()[0] ; // ファイル名から引数を生成する var tempArgv = parseArguments(decodeURIComponent(new File($.fileName).name), argv) ; if(tempArgv == null) { var message = { ja: 'ファイル名から動作を読み取れませんでした。', en: 'Unable to detect action type from file name.' } ; alert(message) ; return ; } var justificationKeys = [ 'LEFT', 'CENTER', 'RIGHT', 'FULLJUSTIFYLASTLINELEFT', 'FULLJUSTIFYLASTLINECENTER', 'FULLJUSTIFYLASTLINERIGHT', 'FULLJUSTIFY' ] ; var justificationValues = [ Justification.LEFT, Justification.CENTER, Justification.RIGHT, Justification.FULLJUSTIFYLASTLINELEFT, Justification.FULLJUSTIFYLASTLINECENTER, Justification.FULLJUSTIFYLASTLINERIGHT, Justification.FULLJUSTIFY ] ; var justificationTable = {} ; for(var i = 0, len = justificationKeys.length ; i < len ; i++) { var currentKey = justificationKeys[i] ; justificationTable[currentKey] = justificationValues[i] ; } var justificationKey ; if(/^dialog$/i.test(tempArgv.option)) { var justificationLocalizeObjects = [ { ja: '左 / 上揃え', en: 'Left Align Text' }, { ja: '中央揃え', en: 'Center Text' }, { ja: '右 / 下揃え', en: 'Right Align Text' }, { ja: '均等配置 (最終行左 / 上揃え)', en: 'Justify Text Left' }, { ja: '均等配置 (最終行中央揃え)', en: 'Justify Text Center' }, { ja: '均等配置 (最終行右 / 下揃え)', en: 'Justify Text Right' }, { ja: '両端揃え', en: 'Justify All Lines' }, ] ; var dialogTitle = 'changeJustification' ; var dialogDesc = { ja: '適用する行揃えを選択', en: 'Select a justification to apply' } ; justificationKey = chooser(justificationLocalizeObjects, justificationKeys, dialogTitle, dialogDesc) ; if(justificationKey == null) {return ;} } else { justificationKey = tempArgv.option ; } newJustification = justificationTable[justificationKey] ; if(newJustification == null) {return ;} for(var i = 0 ; i < targetItemLength ; i++) { var currentFrame = targetItems[i] ; // 行揃え変更 var pos = currentFrame.position ; try { var targetRange = currentFrame.story.textRange ; if(targetRange.justification != newJustification) { if(aiVersion >= 14) { // Illustrator CS4からのバグ(適用されているスタイルの初期値と同じ値をセットしても無視される)を回避して適用する setJustification(targetRange, newJustification) ; } else { // CS3までは普通に変更できるのでそうする targetRange.justification = newJustification ; } // 変わってしまったポイント文字の位置をもとに戻す if(currentFrame.kind == TextType.POINTTEXT) { currentFrame.position = pos ; } } } catch(e) { // 孤立点はエラー。スキップして次へ } } })(arguments) ; /** * スクリプト実行元アプリケーションのバージョンを取得して数値の配列にする。16.0.4の場合[16, 0, 4] * @returns {Array<number>} */ function appVersion() { var tmp = app.version.toString().split('.') ; var res = [] ; for(var i = 0, len = tmp.length ; i < len ; i++) { res.push(Number(tmp[i])) ; } return res ; } /** * selectionからgroupItemの中身を含めたすべてのpageItemを返す * @param {Array<PageItem>} sel selection * @returns {Array<PageItem>} */ function allPageItemOfSelection(sel) { var res = [] ; for(var i = 0, len = sel.length ; i < len ; i++) { var currentItem = sel[i] ; switch(currentItem.constructor.name) { case 'GroupItem' : res.push(currentItem) ; res = res.concat(arguments.callee(currentItem.pageItems)) ; break ; default : res.push(currentItem) ; break ; } } return res ; } /** * Array.filter的な機能 * @param {Array<any> | Collection} targetItems 対象のArrayかcollection。lengthとindexがあれば何でもいい * @param {Function} callback 条件式 * @returns {Array<any?>} */ function filterItems(targetItems, callback) { var res = [] ; for(var i = 0, len = targetItems.length ; i < len ; i++) { if(i in targetItems) { var val = targetItems[i] ; if(callback.call(targetItems, val, i)) {res.push(val) ;} } } return res ; } /** * mode=option.jsxのような文字列を解析した結果 * @typedef ParseArgumentsResult * @property {string} mode mode部分の文字列 * @property {string} option option部分の文字列 */ /** * mode=option.jsxのような文字列を解析する * @param {string} str 対象の文字列 * @param {Arguments} argv ファイル実行引数 * @returns {ParseArgumentsResult} */ function parseArguments(str, argv) { var res ; var matchObj = str.match(/^(justification)[\s ]*[==][\s ]*(argv|dialog|FULLJUSTIFYLASTLINELEFT|FULLJUSTIFYLASTLINECENTER|FULLJUSTIFYLASTLINERIGHT|FULLJUSTIFY|LEFT|CENTER|RIGHT)[\s ]*(?:\.js(?:x|xbin)?)?$/i) ; if(!matchObj) {return res ;} var mode = matchObj[1] ; mode = mode.toLowerCase() ; var opt = matchObj[2] ; // 単一ファイルで引数を受け取るパターンに対応する var optionText ; if( /^argv$/i.test(opt) ) { var optionText = $.getenv('sttk3_arguments') ; if(optionText == '') { if(argv.length <= 0) {return res ;} optionText = argv[0].toString() ; } } else { optionText = opt ; } res = {mode: mode, option: optionText.toUpperCase()} ; return res ; } /** * TextRangeに行揃えをセットする * @Param {TextRange} textRange - 対象のTextRange * @Param {Justification} dstJustification - 適用する行揃え */ function setJustification(textRange, dstJustification) { var appliedParagraphStyle = textRange.paragraphStyles[0] ; var targetPropName = 'justification' ; var styleJustification = appliedParagraphStyle[targetPropName] ; var swapTable = { 'Justification.LEFT': Justification.FULLJUSTIFYLASTLINELEFT } ; var secondOpinion = Justification.LEFT ; if(styleJustification === dstJustification) { // 適用が無視されるパターンの場合、スタイルを変更してから行揃えを変更し、スタイルをもとに戻す var modified = false ; try { // ここでユーザーが察知不可能な段落スタイルの編集を行うので、失敗したときも確実にもとに戻す必要がある appliedParagraphStyle[targetPropName] = swapTable[dstJustification.toString()] || secondOpinion ; modified = true ; // 行揃えを変更する textRange[targetPropName] = dstJustification ; } finally { // 段落スタイルの編集を確実にもとに戻す if(modified) { appliedParagraphStyle[targetPropName] = styleJustification ; } } } else { // 普通に行揃えを変更できる場合は、普通に変更する textRange[targetPropName] = dstJustification ; } } /** * {ja: '', en: ''} のような翻訳文字列用オブジェクト * @typedef LocalizeObject * @property {string} ja - 日本語文字列 * @property {string} en - 英語文字列 */ /** * リストにある選択肢をダイアログで1つ選ぶ * @param {Array<string | LocalizeObject>} listForDisplay - ダイアログに表示する選択肢 * @param {Array<any>?} listForReturn - 選択肢をもとに実際に返す値の配列 * @param {(string | LocalizeObject)?} title - ダイアログに表示するウインドウタイトル * @param {(string | LocalizeObject)?} description - ダイアログに表示する説明 * @returns {string} */ function chooser( listForDisplay, listForReturn, title, description ) { // config var hotKeys = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ; var delim = '\t' ; var res ; if(listForReturn == null) { listForReturn = listForDisplay ; } else { if(listForDisplay.length != listForReturn.length) { throw new Error({ ja: '表示用と返却用の配列の数は同じにする必要があります。', en: 'Number of arrays for display and return must match.' }) ; } } if(title == null) { title = { ja: '選択', en: 'Select' } ; } if(description == null) { description = { ja: '一覧から選択', en: 'Select an item' } ; } // 一覧を作る var commandList = [] ; for(var i = 0, len = listForDisplay.length ; i < len ; i++) { commandList.push((hotKeys[i] || ' ') + delim + listForDisplay[i]) ; } if(commandList.length <= 0) {return res ;} // ダイアログ var win = new Window('dialog', title) ; // リスト var body = win.add('group', undefined) ; body.orientation = 'column' ; body.alignChildren = ['fill', 'fill'] ; body.add('statictext', undefined, description) ; var list = body.add('listbox', undefined, commandList) ; list.selection = list.items[0] ; list.active = true ; win.addEventListener('keyup', function(e) { var dstIndex = hotKeys.indexOf(e.keyName) ; if(dstIndex >= 0) {list.selection = list.items[dstIndex] ;} }) ; // Cancel | OK var footer = win.add('group', undefined) ; footer.orientation = 'row' ; footer.alignChildren = ['center', 'fill'] ; var cancelButton, okButton ; var os = $.os ; var cancelLabel = 'Cancel' ; var okLabel = 'OK' ; if(/^w/i.test(os)) { okButton = footer.add('button', undefined, okLabel) ; cancelButton = footer.add('button', undefined, cancelLabel) ; } else { cancelButton = footer.add('button', undefined, cancelLabel) ; okButton = footer.add('button', undefined, okLabel) ; } okButton.selected = true ; var doCancel = false ; okButton.onClick = okButton.onSubmit = function(e) {win.close() ;} ; cancelButton.onClick = function(e) { doCancel = true ; win.close() ; } ; // ダイアログを表示する win.show() ; if(doCancel) {return res ;} res = listForReturn[list.selection.index] ; return res ; } |
このサイトで配布しているスクリプトやその他のファイルを、無断で転載・配布・販売することを禁じます。
それらの使用により生じたあらゆる損害について、私どもは責任を負いません。
スクリプトやファイルのダウンロードを行った時点で、上記の規定に同意したとみなします。



