/**
* @file PIXTAのベクター素材NGチェック
* @version 1.0.1
* @author sttk3.com
* @copyright (c) 2019 sttk3.com
*/
//#target 'illustrator'
(function() {
$.localize = true ;
// Illustrator CS6以前は終了する。高速化のため
var aiVersion = appVersion()[0] ;
if(aiVersion < 16) {
alert({en : 'This script requires Illustrator CS6 or later', ja : 'このスクリプトは Illustrator CS6 以降のみに対応しています。'}) ;
return ;
}
// 時間計測用
//var startDate = new Date() ;
if(app.documents.length <= 0) {return ;}
var doc = app.documents[0] ;
doc.pageOrigin = doc.rulerOrigin = [0, 0] ;
// documentがもつcollectionはグループや複合パスの中身も拾うが,各レイヤーのcollectionをたどるより遅い
var rasterImages = doc.rasterItems ;
var lenRaster = rasterImages.length ;
var linkImages = doc.placedItems ;
var lenLink = linkImages.length ;
var pathList = doc.pathItems ;
var lenPath = pathList.length ;
// すべてのレイヤーを編集可能にする
tempAction(actionCode(), function(actionItems) {
actionItems[0].exec(false) ;
}) ;
// ロックされたアイテムをロック解除
unlockAll() ;
// すべての選択を解除
unselectAll() ;
// 非表示のオブジェクトをロック=記録
findLock('showAll') ;
// 孤立点をロック=記録
findLock('Stray Points menu item') ;
// テキストフレームをロック=記録
findLock('Text Objects menu item') ;
// ラスター画像をロック=記録
for(var i = 0 ; i < lenRaster ; i++) {
lockItem(rasterImages[i]) ;
}
// リンク画像をロック=記録
for(var i = 0 ; i < lenLink ; i++) {
lockItem(linkImages[i]) ;
}
var currentItem ;
for(var i = 0 ; i < lenPath ; i++) {
currentItem = pathList[i] ;
if(!currentItem.closed) {
if(currentItem.filled && !currentItem.guides) {
// 塗りのあるオープンパスを記録
lockItem(currentItem) ;
} else if(currentItem.pathPoints.length <= 1) {
// 孤立点を記録
lockItem(currentItem) ;
}
}
}
// ロック解除=対象をすべて選択
unlockAll() ;
// 時間計測用
//alert(new Date() - startDate) ;
// 結果を言葉でも知らせる
var ngLen = doc.selection.length ;
var msg, title ;
if(ngLen <= 0) {
title = {en : 'Safely', ja : '正常'} ;
msg = {en : 'Invalid items were not found.', ja : 'NG項目は見つかりませんでした。'} ;
} else {
title = {en : 'Nomaly Detection', ja : '異常検出'} ;
ngLen = ngLen.toString() ;
msg = {en : 'Invalid item count : ' + ngLen, ja : ngLen + ' 個のNG項目を検出しました。'} ;
}
alert(msg, title) ;
return true ;
})() ;
/**
* スクリプト実行元アプリケーションのバージョンを取得して数値の配列にする。16.0.4の場合[16, 0, 4]
* @return {Array of numbers}
*/
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 ;
}
/**
* すべてのロックを解除する
* @return なし
*/
function unlockAll() {
app.executeMenuCommand('unlockAll') ;
}
/**
* すべての選択を解除する
* @return なし
*/
function unselectAll() {
app.selection = [] ;
}
/**
* 検索コマンドを実行し,選択されたアイテムをロックする(後でまとめて選択できる)
* @param {String} keyStr 検索メニューコマンド
* @return なし
*/
function findLock(keyStr) {
app.executeMenuCommand(keyStr) ;
app.redraw() ;
if(app.selection.length > 0) {app.executeMenuCommand('lock') ;}
}
/**
* アイテムをロックする
* @param {PageItem} aItem 対象のページアイテム
* @return なし
*/
function lockItem(aItem) {
try {
aItem.locked = true ;
} catch(e) {
// skip
}
}
/**
* アイテムを選択する。数が多いとき,app.selection = Arrayより諦めずに続けてくれる
* @param {PageItem} aItem 対象のページアイテム
* @return なし
*/
function selectItem(aItem) {
aItem.selected = true ;
}
/**
* アクションを文字列から生成し実行するブロック構文。終了時・エラー発生時の後片付けは自動
* @param {String} actionCode アクションのソースコード
* @param {Function} func ブロック内処理をここに記述する
* @return なし
*/
function tempAction(actionCode, func) {
// utf8の16進数文字コードをJavaScript内部で扱える文字列に変換する
var hexToString = function(hex) {
var res = decodeURIComponent(hex.replace(/(.{2})/g, '%$1')) ;
return res ;
} ;
// ActionItemのconstructor。ActionItem.exec()を使えばわざわざ名前を直接指定しなくても実行できる
var ActionItem = function ActionItem(index, name, parent) {
this.index = index ;
this.name = name ; // actionName
this.parent = parent ; // setName
} ;
ActionItem.prototype.exec = function(showDialog) {
doScript(this.name, this.parent, showDialog) ;
} ;
// ActionItemsのconstructor。
// ActionItems['actionName'], ActionItems.getByName('actionName'),
// ActionItems[0], ActionItems.index(-1)
// などの形式で中身のアクションを取得できる
var ActionItems = function ActionItems() {
this.length = 0 ;
} ;
ActionItems.prototype.getByName = function(nameStr) {
for(var i = 0, len = this.length ; i < len ; i++) {
if(this[i].name == nameStr) {return this[i] ;}
}
} ;
ActionItems.prototype.index = function(keyNumber) {
var res ;
if(keyNumber >= 0) {
res = this[keyNumber] ;
} else {
res = this[this.length + keyNumber] ;
}
return res ;
} ;
// アクションセット名を取得
var regExpSetName = /^\/name\s+\[\s+\d+\s+([^\]]+?)\s+\]/m ;
var setName = hexToString(actionCode.match(regExpSetName)[1].replace(/\s+/g, '')) ;
// セット内のアクションを取得
var regExpActionNames = /^\/action-\d+\s+\{\s+\/name\s+\[\s+\d+\s+([^\]]+?)\s+\]/mg ;
var actionItemsObj = new ActionItems() ;
var i = 0 ;
var matchObj ;
while(matchObj = regExpActionNames.exec(actionCode)) {
var actionName = hexToString(matchObj[1].replace(/\s+/g, '')) ;
var actionObj = new ActionItem(i, actionName, setName) ;
actionItemsObj[actionName] = actionObj ;
actionItemsObj[i] = actionObj ;
i++ ;
if(i > 1000) {break ;} // limiter
}
actionItemsObj.length = i ;
// aiaファイルとして書き出し
var failed = false ;
var aiaFileObj = new File(Folder.temp + '/tempActionSet.aia') ;
try {
aiaFileObj.open('w') ;
aiaFileObj.write(actionCode) ;
} catch(e) {
failed = true ;
alert(e) ;
return ;
} finally {
aiaFileObj.close() ;
if(failed) {try {aiaFileObj.remove() ;} catch(e) {}}
}
// 同名アクションセットがあったらunloadする。これは余計なお世話かもしれない
try {app.unloadAction(setName, '') ;} catch(e) {}
// アクションを読み込み実行する
var actionLoaded = false ;
try {
app.loadAction(aiaFileObj) ;
actionLoaded = true ;
func.call(func, actionItemsObj) ;
} catch(e) {
alert(e) ;
} finally {
// 読み込んだアクションと,そのaiaファイルを削除
if(actionLoaded) {app.unloadAction(setName, '') ;}
aiaFileObj.remove() ;
}
hexToString = ActionItem = ActionItems = null ;
}
/**
* レイヤーをすべて表示し,ロックを解除するアクションのソースコード
* @return {String}
*/
function actionCode() {
return '''/version 3
/name [ 15
5f5f7374746b335f6c617965725f5f
]
/isOpen 1
/actionCount 1
/action-1 {
/name [ 10
746f4564697461626c65
]
/keyIndex 0
/colorIndex 0
/isOpen 1
/eventCount 2
/event-1 {
/useRulersIn1stQuadrant 0
/internalName (ai_plugin_Layer)
/localizedName [ 12
e383ace382a4e383a4e383bc
]
/isOpen 1
/isOn 1
/hasDialog 0
/parameterCount 3
/parameter-1 {
/key 1836411236
/showInPalette 4294967295
/type (integer)
/value 9
}
/parameter-2 {
/key 1937008996
/showInPalette 4294967295
/type (integer)
/value 22
}
/parameter-3 {
/key 1851878757
/showInPalette 4294967295
/type (ustring)
/value [ 42
e38199e381b9e381a6e381aee383ace382a4e383a4e383bce38292e383ade383
83e382afe8a7a3e999a4
]
}
}
/event-2 {
/useRulersIn1stQuadrant 0
/internalName (ai_plugin_Layer)
/localizedName [ 12
e383ace382a4e383a4e383bc
]
/isOpen 1
/isOn 1
/hasDialog 0
/parameterCount 3
/parameter-1 {
/key 1836411236
/showInPalette 4294967295
/type (integer)
/value 7
}
/parameter-2 {
/key 1937008996
/showInPalette 4294967295
/type (integer)
/value 17
}
/parameter-3 {
/key 1851878757
/showInPalette 4294967295
/type (ustring)
/value [ 33
e38199e381b9e381a6e381aee383ace382a4e383a4e383bce38292e8a1a8e7a4
ba
]
}
}
}''' ;
}
したたか企画様
素晴らしいスクリプトありがとうございます。
とても重宝しております。
一つご提案なのですが、
チェック終了時にもしNG項目があった場合に
「NGあり」「NGなし」のアラートを数秒表示してもらあえと嬉しいのですが、
難しいでしょうか。
孤立点が一個だけあったときなどに見逃しを減らせそうですので
ぜひご検討のほどよろしくお願いいたします。
ご感想ありがとうございます。
NGあり/なしでメッセージの文言を変えるのは簡単にできます。違ったほうが親切ですね。アラートの秒数を長くするみたいな意味なら難しいです。
とりあえずメッセージの文言は変えてみます。
もう修正してくださったんですね。
しかもNG項目の残数も表示も!
このスクリプトのおかげで作業中に定期的にチェックする回数が増え
効率がUPしております。
本当にありがとうございます!
役に立っているようで良かったです。
周りのかたにもどんどん布教してください。