2009年12月31日木曜日

SpringSource Tool Suite (Eclipse) でGrailsの.propertiesファイルの日本語が化ける件。

正月休みにGrailsを再勉強しようと思い、せっかくなので SpringSource Tool Suite(以下STS)を使ってみようと思った。

STSの使い方、Groovy,Grails環境の設定については、tyama氏が詳しい説明を書いてくれています。
SpringSource Tool Suite 2.2.0でGrailsをためしてみた。 - leftovers...

ダウンロードしてインストールして、設定して、create-appして、順調に行っていたのだが、i18nの messeages_ja.properties の中にある日本語が、全部化けているのに気がつく。

日本語の設定場所を探したが見つからない。そういえばこれもEclipseなんだから、と思ってEcripseをキーワードに検索したら、なんだかいろいろこみいった事情があることが分かった。
  • Javaでは、Propertiesクラスは7バイトまでしか対応していない(Java6でマルチバイトに対応)。
  • .propertiesのエンコーディングはISO 8859-1が使用される。
  • Javaの場合、.propertiesで日本語を使用する場合は、native2asciiを使って、Unicodeコードに変換する。
そんなわけで、Eclipse標準のProperties File EditorはUTF8の日本語を開けないのが仕様みたい。

Grailsの.propertiesファイルには日本語をそのまま書いていいはず(create-appして生成されるファイルには日本語そのまま書いてある)ので、これでは困る。
Eclipseのいろんなサイトに書いてあったプロパティエディタを入れることにした。

http://sourceforge.jp/projects/propedit/
から、jp.gr.java_conf.ussiy.app.propedit_4.8.2_for_eclipse3.0.zip を落として解凍する。
できたフォルダの、featuresの中身をspringsource/sts-2.3.0.RELEASE/features/、
pluginsの中身をspringsource/sts-2.3.0.RELEASE/plugins/の中に入れる。
STSを再起動し、環境設定の
General > Editors > File Associations から *.properties のデフォルトエディタにする。

プロパティエディタ から、変換オプションで"全てユニコード変換しない"にチェック。

これで日本語が表示される。OK。

2009年12月21日月曜日

XmlSlurperで属性をそのままテキストだけを入れ替える。

GroovyではXML処理に、XmlPerserとXmlSlurper(他いろいろ……)が用意されてあって、ただ読み込む場合はXmlSlurper、編集するなり書き換える場合はXmlPerserを使うように、って棲み分けがされている。
だけどSlurperでもいちおう簡単な編集機能は用意されてるみたいで、
XmlSlurper を使った XML の加工 - なんとなくな Developer のメモ
  • 要素の追加: appendNode メソッドを使用
  • 属性の追加: @属性名 に値を設定
  • 要素の置換: replaceNode メソッドを使用
  • 要素の削除: replaceNode メソッドを使用(実装を空にする)

となるらしい。なのでこれを使ってXMLのテキストを入れ替えようと思った。
import groovy.xml.StreamingMarkupBuilder

String myXml='''<root>
<species order="甲虫目" family="コガネムシ科">カブトムシ</species>
<species order="甲虫目" family="カミキリムシ科">シロスジカミキリ</species>
<species order="チョウ目" family="アゲハチョウ科">アゲハチョウ</species>
</root>'''

def doc = new XmlSlurper().parseText(myXml)

doc.species[0].replaceNode{species('クロカナブン')}

//テキストに戻す
StreamingMarkupBuilder builder = new StreamingMarkupBuilder()
builder.encoding = "UTF8"
def newXml = builder.bind{mkp.yield doc};

println(newXml)
こうすると、当然「カブトムシ」の要素ごと書き変わってしまうので、クロカナブンの目と科がわからなくなってしまう。少し(かなり)考えて、属性を保存して、新しく入れ替える要素にコピーできるんじゃないかな?
import groovy.xml.StreamingMarkupBuilder

String myXml='''<root>
<species order="甲虫目" family="コガネムシ科">カブトムシ</species>
<species order="甲虫目" family="カミキリムシ科">シロスジカミキリ</species>
<species order="チョウ目" family="アゲハチョウ科">アゲハチョウ</species>
</root>'''

def doc = new XmlSlurper().parseText(myXml)
//assert doc.species[0] == 'カブトムシ'

Map attr = doc.species[0].attributes();
//assert attr == [family:'コガネムシ科', order:'甲虫目']

//ノードを置き換える時に属性をそのままコピー
doc.species[0].replaceNode{species('クロカナブン',attr)}

//テキストに戻す
StreamingMarkupBuilder builder = new StreamingMarkupBuilder()
builder.encoding = "UTF8"
def newXml = builder.bind{mkp.yield doc};

println(newXml)
これでOK。属性のマップをそのまま後ろに入れるだけで属性になる。へー。
結果はこう。
<root><species family='コガネムシ科' order='甲虫目'>クロカナブン</species><species family='カミキリムシ科' order='甲虫目'>シロスジカミキリ</species><species family='アゲハチョウ科' order='チョウ目'>アゲハチョウ</species></root>

groovyでWikiTextを使ってみる

仕事が煮詰まって頭モウロウとしながらひたすら検索してると、ちょっと気になるブログ記事を見つけた。
WikiTextことはじめ - Talking Nonstop
WikiTextとは、Javaで書かれたWikiパーサです。

なにこれ面白そう。Groovyから叩けるかな?そう思ってやってみた。
まず、MylynのページからWikiTextスタンドアロン版(mylyn-wikitext-standalone-latest.zip)を落として解凍、中に入ってる.jarを全て~/.groovy/lib/につっこむ。
import org.eclipse.mylyn.wikitext.core.parser.MarkupParser
import org.eclipse.mylyn.wikitext.confluence.core.ConfluenceLanguage
import org.eclipse.mylyn.wikitext.core.parser.builder.HtmlDocumentBuilder

String markupContent='''h1. 見出し1
ここは見出し1のテキスト部分

h2. 見出し2
ここは見出し2のテキスト部分

h3. 見出し3
ここは見出し3のテキスト部分
* 箇条書き1行目
* 箇条書き2行目
* 箇条書き3行目
** 3行目の1
** 3行目の2

# 番号つき1行目
# 番号つき2行目
# 番号つき3行目'''

StringWriter writer = new StringWriter();
HtmlDocumentBuilder builder = new HtmlDocumentBuilder(writer);

MarkupParser parser = new MarkupParser(new ConfluenceLanguage());
parser.setBuilder(builder);
parser.parse(markupContent);

String htmlContent = writer.toString();
println(htmlContent)
それの結果。
<?xml version='1.0' encoding='utf-8' ?><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/></head><body><h1 id="a1">見出し1</h1><p>ここは見出し1のテキスト部分</p><h2 id="a2">見出し2</h2><p>ここは見出し2のテキスト部分</p><h3 id="a3">見出し3</h3><p>ここは見出し3のテキスト部分</p><ul><li>箇条書き1行目</li><li>箇条書き2行目</li><li>箇条書き3行目<ul><li>3行目の1</li><li>3行目の2</li></ul></li></ul><ol><li>番号つき1行目</li><li>番号つき2行目</li><li>番号つき3行目</li></ol></body></html>

楽しい!いつか何か作ろう。

2009年12月14日月曜日

InDesignでガイド線に合わせて表を作成する。

何がやりたかったのかというと、これです。

様式のトレース。セルが複雑になってくると、意外と難しいんですよこれ。
これの線に合わせてガイドを引いて、表を作成した後余分な線を抜こうという計画。


ガイド線を引いた後、このスクリプトを実行すると

こんな感じに。(見やすくなるように、バックを薄くして、線を太くしてあります。)
InDesignCS3,MacOSX10.5.8上で動作確認。

2009年12月7日月曜日

なるほどreturnすりゃ止まるんだ

たぶん、分かってる人から見れば今更なエントリ。
var fol=Folder.selectDialog("フォルダを選択");
if(!fol){
alert("処理を中止しました。");
}else{

/*
















*/

}
みたいなコードを書いた事ないだろうか。
前に先輩から「こういう長いカッコ使うのやめなよ」と言われていたのだが、 そのときは実際どうすればいいのかは教わらなかったし、いままでどうするかも思いつかなかった。

ところで、人の書いてるスクリプトを読んでいるとたまに
(function(){
//処理
})();
みたいな感じに、全体をfunctionでつつみこんでそのまま実行しているスクリプトを見る。
この書き方については「組版時間を半減する! InDesign自動処理実例集」の99ページにふれてあって
 これは、各プログラム中で使用されている変数名などで共通のものがあるために、同時にサンプルプログラムを使用する際に干渉しないようにするためのものです。

ということで、functionの中で変数をvar宣言してあればその宣言はその中の範囲にしか適用されない。そういうもんなんだろなと思ってた。

ところがAdobeのサンプルコードを読んでいて、別の効果があることに気づいた。
(function(){
 var fol=Folder.selectDialog("フォルダを選択");
if(!fol){
alert("処理を中止しました。");
return;
}
//処理
})();
returnすればScriptを抜けられる!これで長い{}を書いて、カッコの閉じ忘れを気にしなくてすむ。
なるほどーー、そうかー。

2009年11月13日金曜日

InDesignのスクリプトでパスファインダを使う

ちょっと調べたことのメモ。
InDesignにも、Illustratorと同じようにパスファインダがある。

使用するには、オブジェクトのひとつを選択してメソッドを実行、引数に結合したいオブジェクトを配列で渡す。
var sels=app.activeDocument.selection;
var sel=sels.shift();

sel.addPath(sels);



種類は次の通り
  • PageItem.addPath (with:Array of PageItem )
    結合
  • PageItem.excludeOverlapPath (with:Array of PageItem )
    中マド
  • PageItem.intersectPath (with:Array of PageItem )
    交差
  • PageItem.minusBack (with:Array of PageItem )
    背面オブジェクトで型抜き
  • PageItem.subtractPath (with:Array of PageItem )
    前面オブジェクトで型抜き

2009年11月11日水曜日

if 文と switch 文の条件部分を関数で分ける。

Illustrator用のスクリプトを書いていて、if文の条件がやたらに長くなってしまい、どうしようかと思うことがあった。
例として、「塗りがC10、またはM10、またはY10のときは塗りを白に、塗りがC100,M100のときは塗りを黒にする」という変換を書いていたとする。
だいたいこんな感じになった。
#target "Illustrator"
var objs=app.activeDocument.selection;

for(var i=0;i<objs.length;i++){
var color=objs[i].fillColor;

if((Math.round(color.cyan)==10 && color.magenta==0
&& color.yellow==0 && color.black==0)
||(color.cyan==0 && Math.round(color.magenta)==10
&& color.yellow==0 && color.black==0)
||(color.cyan==0 && color.magenta==0
&& Math.round(color.yellow)==10 && color.black==0)){

color.cyan=0;
color.magenta=0;
color.yellow=0;
color.black=0;

}else if((color.cyan==100 && color.magenta==0
&& color.yellow==0 && color.black==0)
||(color.cyan==0 && color.magenta==100
&& color.yellow==0 && color.black==0)){

color.cyan=0;
color.magenta=0;
color.yellow=0;
color.black=100;
};

};
実際はもうちょっと複雑で、書きながらだからコードも汚い。見にくくてしょうがない。
ふと、「if文の答えは結局trueかfalseなんだから、その部分を分けてやればいいのでは?」と思い立ち、書いてみた。
下のような感じ。
#target "Illustrator"
// ----------if条件の定義----------
var check1=function(color){
var c=Math.round(color.cyan);
var m=Math.round(color.magenta);
var y=Math.round(color.yellow);
var k=Math.round(color.black);
var flag=false;
if(c==10 && m==0 && y==0 && k==0){
flag=true;
}else if(c==0 && m==10 && y==0 && k==0){
flag=true;
}else if(c==0 && m==0 && y==10 && k==0){
flag=true;
};
return flag;
};
var check2=function(color){
var c=Math.round(color.cyan);
var m=Math.round(color.magenta);
var y=Math.round(color.yellow);
var k=Math.round(color.black);
var flag=false;
if(c==100 && m==0 && y==0 && k==0){
flag=true;
}else if(c==0 && m==100 && y==0 && k==0){
flag=true;
};
return flag;
};
// --------------------
var objs=app.activeDocument.selection;
for(var i=0;i<objs.length;i++){
var color=objs[i].fillColor;

if(check1(color)){
color.cyan=0;
color.magenta=0;
color.yellow=0;
color.black=0;
}else if(check2(color)){
color.cyan=0;
color.magenta=0;
color.yellow=0;
color.black=100;
};

};
全体としては長くなったけれど、コードの役割がきちんと分割できて、ちょっと読みやすくなったと思う。
そこで「if文でできるならswitch-caseでもできるんじゃ?」と思って試す。
まずcaseの部分にfunctionを書き込んでみたが、上手くいかない。
switch-caseの場合はswitch(〜)の部分とcase 〜:の部分が同じかどうかをチェックして、trueなら処理をするという形だから。
ネットを探していると、phpの人がswitchに関数を使っているのを見つけた。
zuzara.com » PHPのswitchのcase文には関数が使える。
memo.xight.org - switch ,case文を読みやすくする
それで書いたのが以下のコード。
#target "Illustrator"

//----- check1、check2 は同上 -----

var objs=app.activeDocument.selection;
for(var i=0;i<objs.length;i++){
var color=objs[i].fillColor;

switch(true){ //trueで固定してしまう
case check1(color):
color.cyan=0;
color.magenta=0;
color.yellow=0;
color.black=0;
break;
case check2(color):
color.cyan=0;
color.magenta=0;
color.yellow=0;
color.black=100;
break;
default:
break;
};

};

2009年11月9日月曜日

InDesignを使ってPDFをページごとに分割

PDFをページごとに分割するのに、InDesignを使ってみる。
こういうことにInDesignを使う事はまず無い。普通はAcrobatや、ほかの便利なフリーウェアとか使うよね?
それでもInDesignでやりたいと思う事もあります。
#target "InDesign"

var pdfFile=File.openDialog ("PDFを選択して下さい", pdfCheck, false);
if(pdfFile){
//pdfを分割
var pageFileAry=burstPDF(pdfFile);
alert(pageFileAry.length+"ページにpdfを分割しました。");
};

function burstPDF(pdfFile){
//$.writeln(pdfFile);
//PDF読み込み設定:メディアサイズ
app.pdfPlacePreferences.pdfCrop=PDFCrop.CROP_MEDIA;
var actDoc=app.documents.add();
var imgObj= actDoc.textFrames.add();
//1ページ目を取り込む
var i=1
app.pdfPlacePreferences.pageNumber = i;
var ppdf=imgObj.place(pdfFile);
//$.writeln(ppdf);
//フィット
imgObj.fit(FitOptions.FRAME_TO_CONTENT);
//ページサイズを合わせる
var bounds=imgObj.geometricBounds;
actDoc.documentPreferences.pageWidth=bounds[3]-bounds[1];
actDoc.documentPreferences.pageHeight=bounds[2]-bounds[0];
imgObj.move([0,0]);
//pdf書き出し
var ary=[];
do{
var flag=true;
try{
var burstPdfPath=cutEx(pdfFile.fsName)[0]+"_"+i+".pdf";
var burstPdf=new File(burstPdfPath);
//$.writeln(burstPdfPath);
actDoc.exportFile(ExportFormat.pdfType, burstPdf, false, "PDFX1a 2001 JPN");
ary.push(burstPdfPath);
i++;
app.pdfPlacePreferences.pageNumber = i;
imgObj.place(pdfFile);
if(app.pdfPlacePreferences.pageNumber!=i){flag=false};
}catch(e){
//$.writeln(e);
flag=false
};
}while(flag);
actDoc.close(SaveOptions.NO);
return ary;
};

function pdfCheck(file){
//PDFを選択するフィルタ
var flag=false;
if(file.name.match(/\.pdf$/) || file instanceof Folder){flag=true};
return flag;
};

function cutEx(path){
//拡張子を切る
var pathTxt=path, exText="";
var dirCutNo=( (/\//.test(path))? path.lastIndexOf("/") : 0 );
if(/\./.test(path.slice(dirCutNo))){
pathTxt=path.slice(0, path.lastIndexOf("."));
exText=path.slice(path.lastIndexOf("."));
};
return [pathTxt,exText];
};

2009年10月20日火曜日

JavaScript と、Perl と、Groovy の split

ある文字でデータを分けて配列にするsplit。例えば JavaScirpt で
var text = "AAA<tag>BBB</tag>CCC";
var ary = text.split(/<.+?>/);
$.writeln(ary.toSource());
//ESTK の場合。 fireBug ならば console.log(ary);

["AAA", "BBB", "CCC"]
と表示される。
それが、splitの中で使う正規表現をキャプチャして
var ary = text.split(/(<.+?>)/);
とすると結果が
["AAA", "<tag>", "BBB", "</tag>", "CCC"]
と返る!このことを自分は今日まで知らなかった。

perlでも同じように返る。
my $text = "AAA<tag>BBB</tag>CCC";
my @ary = split(/(<.+?>)/,$text);
foreach $token(@ary){
print $token . "\n";
}
AAA
<tag>
BBB
</tag>
CCC

Rubyでも同じらしい。じゃあGroovyだと思い、
def text = "AAA<tag>BBB</tag>CCC"
def ary = text.split(/(<.+?>)/)
println ary
とやってみたが、
[AAA, BBB, CCC]
としか返らない。あれ?

Groovy の split は Java の split で、そういう風には使わないっぽい。
じゃあ tokenize では?
def text = "AAA<tag>BBB</tag>CCC"
def ary = text.tokenize(/(<.+?>)/)
println ary
[AAA, tag, BBB, /tag, CCC]
……。
タグの<>がなぜか外れてしまう。むー、よくわからん……。


10月21日追記。
Groovy の ソースの tokenize を読んでみた。DefaultGroovyMethods.java の 6627 行目あたりに
public static List tokenize(String self, String token) {
return InvokerHelper.asList(new StringTokenizer(self, token));
}
つまり、渡された引数が String でも GString でも、そのまま StringTokenizer に渡して List で返しているわけか。(ただの区切り文字の集まりとして読んでいるから、< と > がひっかかって分割されて、< > 自体は消えている)
GString でキャプチャされてた時にデリミタを返すのは難しいにしても、だったら tokenize(String, Boolean) でトークンを返すのは欲しいなぁ。

2009年10月17日土曜日

Illustratorのリンク画像を相対パスで使うスクリプト

Illstratorのリンク画像へのパスは絶対パスで保存されています(使っている人でも知らない人が多い!)。
Illustratorで、貼り込み画像をリンクとして使いたい時、よく下の階層にフォルダを作ってまとめてあるのを見ますが、それだと上のフォルダ名が変わったり、ドライブを移動したりするとリンク切れするんですよね。
それならまだいいんですが、フォルダごとコピーして、コピー側を開くと元のフォルダへのリンクが残っていて、画像を修正したと思ってても元に戻っちゃってたりとか……。

それらを解消できないかと思ってちょっと書いてみました。相対パスを画像に保存しておくスクリプト。
作業したあと、必ずこのスクリプトをかけておけば有効かもしれない。

【相対パスを保存_再リンク.jsx】
#target "Illustrator"
if(app.documents.length>0){
var actDoc=app.activeDocument;
if(actDoc.path==""){
//未保存ならばpathがない
alert("ファイルを保存して下さい");
}else{
var reLinkLog="";
var saveLinkLog="";
//ドキュメントの親フォルダのパス
var docPath=File.decode(actDoc.path);
//アイテムをループ
for(var i=0;i<actDoc.pageItems.length;i++){
//もしリンク画像なら
if(actDoc.pageItems[i].constructor.name=="PlacedItem"){
var item=actDoc.pageItems[i];
//メイン処理を実行
var log=main(item,docPath);
if(log[0]){reLinkLog+=("\n"+log[0])};
if(log[1]){saveLinkLog+=("\n"+log[1])};
};
};
//リンク修正アラート
if(reLinkLog!=""){
app.redraw();
alert("以下のリンクを修正しました。"+reLinkLog);
};
//リンク保存アラート
if(saveLinkLog!=""){
alert("以下の相対パスを保存しました。"+saveLinkLog);
};
};
};

function main(item,docPath){
//メイン処理
var ary=[undefined,undefined];
//「name」の値があれば
if(item.name!=""){
var beforeFilePath=docPath+"/"+File.decode(item.name);
var beforeFile=new File(beforeFilePath);
if(beforeFile.exists){
var flag=false;
try{
//ファイルが相対パスの場所と違っていたら
var nowFilePath=File.decode(item.file);
if(nowFilePath!=beforeFilePath){
flag=true;
}else{};
}catch(e){
//ファイルがなければ
flag=true;
};
//flagがtrueならばリンク貼り直し
if(flag){
item.file=beforeFile;
ary[0]=beforeFilePath;
};
};
};
//nameにファイルの相対パスを入れる
try{
var relativePath=item.file.getRelativeURI(docPath);
item.name=relativePath;
ary[1]=File.decode(relativePath);
}catch(e){};
return ary;
};
IllustratorのPlacedItemオブジェクトの、name属性に相対パスをそのまま保存しています。
なので他のnameを使うスクリプトと併用はできません。
MacOSX 10.5.8、 Illustrator CS3にて動作確認。

【使い方】
  1. Illustratorを編集して、最後に保存する前に、あらかじめこのスクリプトをかけておきます。
  2. ファイルを開いた時に『「〜」というリンクファイルが見つかりません』というダイアログが出た場合は、全て「無視」を選択します。
  3. 開いた後再びスクリプトを実行します。

2009年10月15日木曜日

XmlSlurperでどの階層にあるかわからない要素を取り出す。

ようやくわかったのでメモ。
def myXml = '''<?xml version="1.0" encoding="UTF-8" ?>
<root>
<a>
<target>ここだよ!</target>
</a>
<a>
<b>
<target>ここよ!</target>
</b>
</a>
<a>
<b>
<c>
<target>ここです!</target>
</c>
</b>
</a>
</root>'''

def node = new XmlSlurper().parseText(myXml)
node.'**'.grep{it.name() == 'target'}.each{
println it
}

2009年10月12日月曜日

JavaScriptの{}の使い方のこと。

この間スクリプトでひさしぶりにswitch文を書いていたとき、そういえばswitch文てあんまり使わないなと思った。
それは、自分がいまの会社でスクリプトを書き始めたすごく最初の頃に、「ある変数が持つ数値に対して、ある文字を返す」処理、たとえばv=1ならばж、2ならばз、3ならばи…といったふうな処理を書こうとしていて、たしかこんな感じだったと思う。
function test(v){
var cr;
if(v){
switch(v){
case 1:cr="ж";break;
case 2:cr="з";break;
case 3:cr="и";break;
case 4:cr="й";break;
case 5:cr="к";break;
case 6:cr="л";break;
case 7:cr="м";break;
case 8:cr="н";break;
case 9:cr="п";break;
default:cr="";
};
};
return cr;
};

var no=4;
alert(test(no));
そのコードを見た先輩が、「それだとコードが長くなって見にくくなるから」と言って、こんな風に書き直した。
function test(v){
 var cr="";
 var pat={1:"ж",2:"з",3:"и",4:"й",5:"к",6:"л",7:"м",8:"н",9:"п"};
if(v){cr=pat[v]};
return cr;
};

var no=4;
alert(test(no));
???
JavaScriptのオブジェクトは、
var obj=new Object();
と作るけれど、単純にパラメータを保持するだけなら
var obj={};
でよい。そして、JavaScriptのオブジェクトはほとんどハッシュマップなので、obj[key]で欲しい値が返るわけ。

この件で、{}の使い方と「短いほうが良いコード」という考え方を教わってから、対応する値が1対1ならばほとんどswitch文は書かないようになった。
そのせいで時々switch文を書かなきゃいけない時に、使い方を忘れてたりする。

2009年10月11日日曜日

ローカル上で、XmlSlurper で xhtml を読み込む時はフィーチャーを指定すること

Groovy で、xhtml を XmlSlurper で処理しようと思い、
def src = args[0]
def htmlFile=new File(src)
String str = htmlFile.getText("UTF8")

def node = new XmlSlurper().parseText(str)
//以下略
のようにやってみたら

Caught: java.io.IOException: Server returned HTTP response code: 503 for URL:
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
at xmlSlurperTest.run(xmlSlurperTest_2.groovy:4)
というエラーが出て止まる。
ローカル上でやっているので、xhtml の DTD を読みに行って503ってことらしい。
隣に居たO部長に
「〜となるんですけど、DTD を読みに行かずにする設定ってどうやるのか知りませんか?」
「ヘッダ読み込まなきゃいいんじゃないの?」
そうですけど…
「もういいです自分で調べます」
って言って Groovy の JavaDoc 見に行こうとしてたら方法をメッセンジャーで送ってもらえた。

def src = args[0]
def htmlFile=new File(src)
String str = htmlFile.getText("UTF8")

def parser = new XmlSlurper()
//↓フィーチャーの指定。これを入れないと DTD を読みにいってエラーになる。
parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
def node = parser.parseText(str)
//以下略
フィーチャー?
Java 入門 | DOM (Document Object Model)
groovy.util.XmlSlurper は org.xml.sax.helpers.DefaultHandler のサブクラスで、基本的にはSAXみたい。
デフォルトで true なのか。ふむ。

2009年10月5日月曜日

スクリプトでInDesignの文字組みアキ量設定を作る。

まず、自分の文字組みアキ量設定を作ります。
※サンプルに、n-yujiさんの
僕が勝手に考える理想の(というか妥当な)InDesign文字組版。 - 遠近法ノート
の設定をお借りしました。

文字組みを設定した .indd を開いた状態で、ExtendScript Toolkit から、以下のスクリプトを実行。
var myMojikumi=app.activeDocument.mojikumiTables.itemByName("Y汎用括弧半角v2.0/0808");
$.writeln(myMojikumi.basedOnMojikumiSet);
$.writeln(myMojikumi.overrideMojikumiAkiList
.toSource().replace(/\], \[/g,"],\n ["));

すると、こんな感じでコンソールに表示されます。

わー。
これをスクリプトに組み込みます。
var actDoc = app.activeDocument;
var myMojikumi=actDoc.mojikumiTables.add("Y汎用括弧半角v2.0/0808");
//MojikumiTableDefaults.LINE_END_ALL_ONE_HALF_EM_ENUM
myMojikumi.basedOnMojikumiSet = 1246572593;
//コンソールに表示された配列をそのままコピペ
myMojikumi.overrideMojikumiAkiList=[
[18, 22, false, 0.05, 0.05, 0.05, 0, false],
[18, 23, false, 0.05, 0.05, 0.05, 0, false],
[18, 22, true, 0.05, 0.05, 0.05, 0, false],
[12, 12, false, -0.05, 0, 0.5, 5, false],
[33, 12, false, -0.05, 0, 0.5, 5, false],
[11, 12, false, -0.05, 0, 0.5, 5, false],
[3, 12, false, -0.05, 0, 0.5, 5, false],
[9, 12, false, -0.05, 0, 0.5, 5, false],
[8, 12, false, -0.05, 0, 0.5, 5, false],
[4, 12, false, -0.05, 0, 0, 5, false],
[7, 12, false, -0.05, 0, 0.5, 5, false],
[12, 33, false, -0.05, 0, 0.5, 5, false],

//...中略...

[24, 4, false, 0, 0, 0.5, 0, false],
[25, 18, false, 0, 0, 0.25, 6, false],
[18, 18, false, 0, 0, 0.25, 6, false],
[18, 25, false, 0, 0, 0.25, 6, false],
[25, 25, false, 0, 0, 0.25, 6, false]];
空のドキュメントを開いた状態で実行すれば、新しく文字組みアキ量設定ができます。

2010-02-22追記:
めんどくさいので、読み出し・組み込み部分をまとめて、スクリプトを生成するようにしました。
文字組みを読み取りたい部分のテキストを選択して、スクリプトを実行して下さい。
ファイル保存ダイアログが開き、スクリプトを保存します。
(function(){
if(app.activeDocument.selection.length==0) return;
 var obj=app.activeDocument.selection[0];
var check={
"Text":true,
"TextColumn":true,
"Paragraph":true,
"InsertionPoint":true,
"Character":true,
"TextStyleRange":true
}
if(!check[obj.constructor.name]) return;

var defFile = new File("~/Desktop/mojikumi_"+obj.mojikumi.name+".jsx");
defFile = defFile.saveDlg('スクリプトの保存先を選択して下さい。');
if(!defFile) return;
var mojikumiAryTxt = obj.mojikumi.overrideMojikumiAkiList.toSource();
mojikumiAryTxt = mojikumiAryTxt.replace(/\], \[/g,"],\n [");

var script='var actDoc = app.activeDocument;\n'
+'var myMojikumi=actDoc.mojikumiTables.add("'+ obj.mojikumi.name +'");\n'
+'myMojikumi.basedOnMojikumiSet = '+ obj.mojikumi.basedOnMojikumiSet +';\n'
+'myMojikumi.overrideMojikumiAkiList =\n'+ mojikumiAryTxt +';';

if(defFile.open('w')){
defFile.writeln(script);
defFile.close();
alert("スクリプトを書き出しました。");
}
})();

2009年9月24日木曜日

groovy の eachWithIndex

前々回のエントリについて、上司からダメ出しをもらう。

「kanemu の poi のコード、なんで for 使ってんの?each 使えばいいじゃん」
自分しばし考え
「 i と j が使いたかったんですよ」
「index が欲しかったってこと?じゃあ eachWithIndex は?」
「???」
groovy-jdk を確認すると、eachほにゃららってメソッドがあるわあるわ…。
ためしにgroovyconsoleで
def myVal=[
["あああ","いいい","ううう"],
["かかか","ききき","くくく"],
["さささ","ししし","すすす"]
]

myVal.eachWithIndex{v,i->
println "${i} => ${v}";
v.eachWithIndex{vv,ii->
println "------ ${ii} => ${vv}";
}
}
0 => [あああ, いいい, ううう]
------ 0 => あああ
------ 1 => いいい
------ 2 => ううう
1 => [かかか, ききき, くくく]
------ 0 => かかか
------ 1 => ききき
------ 2 => くくく
2 => [さささ, ししし, すすす]
------ 0 => さささ
------ 1 => ししし
------ 2 => すすす
ははあ、なるほど。
ということは先日のコードは、
import org.apache.poi.hssf.usermodel.*;

def myVal=[
["あああ","いいい","ううう"],
["かかか","ききき","くくく"],
["さささ","ししし","すすす"]
]

//新規ワークブック作成
HSSFWorkbook myWb = new HSSFWorkbook();
//新規シート作成
HSSFSheet mySheet = myWb.createSheet();

myVal.eachWithIndex{v,i ->
//新規行作成
HSSFRow myRow = mySheet.createRow(i);
v.eachWithIndex{vv,ii ->
//新規セル作成
HSSFCell myCel = myRow.createCell((short)ii);
myCel.setEncoding(HSSFCell.ENCODING_UTF_16);
myCel.setCellValue(vv);
}
}
//作成したワークブックを保存する
new File("Test.xls").withOutputStream{myWb.write(it)}
ですね。

2009年9月23日水曜日

TermHere.app を iTermに対応させる。


「TermHere」というAppleScriptアプリが配布されている。
Mac OS X Finder Toolbar Scripts for Textmate, Terminal, and Touch. - Manas Tungare

Finderのツールバーに登録しておけば、開いているフォルダのディレクトリをターミナルで開いてくれる便利なスクリプトなのだが、残念ながら自分がふだん使用しているのはiTerm

なんとなくパッケージの中身を開いてみたら、"Public Domain" と書いてあったので、自分でちょっといじってみた。
  1. まず、上記からTerminalのところをダウンロード
  2. TerminalHere.dmg を開き、中のTermHere.appをコピー
  3. 右クリック「パッケージの内容を表示」で開く
  4. /Contents/Resources/Scripts/main.scpt をスクリプトエディタで開いて編集。
    tell application "Finder"
    set thisFolder to (target of front window) as Unicode text
    set posixPath to quoted form of POSIX path of thisFolder
    end tell

    tell application "iTerm"
    activate
    if (count current terminal) is 0 then
    set myterm to (make new terminal)
    tell myterm
    launch session "Default"
    end tell
    end if
    tell the current session of the current terminal
    write text "cd " & posixPath
    end tell
    end tell
  5. そのまま保存。
  6. 見た目も気にするなら、/Contents/Resources/termhere.icns を iTermのものと入れ替える。

あとは説明書どおりにFinderのツールバーに登録しておけばよい。

2010.07.04追記:
このエントリ久しぶりに見て「これって単にAppleScript書いてるだけじゃん!」と気づいた。
要はアプリケーションバンドルにして保存すればよい。
  1. スクリプトエディタ.appを起動し、上記スクリプトをペースト
  2. アプリケーションバンドルとして保存


これでOK。アイコンの変更は上記を参考に。

2009年9月14日月曜日

groovyとpoiでExcelファイルを出力する

poi は java から Microsoft Office ファイルを扱うライブラリ。これでエクセルファイルを作ってみる。
環境はMacBook(CoreDuo)、MacOSX10.5.8、java v1.5.0_19、groovy v1.6.4。

まず、poiのbinファイルをこちらから入手する。
今回は、poi-bin-3.0-FINAL-20070503.zip をダウンロードした。
解凍して、poi-3.0-rc4-20070503.jar を ~/.groovy/lib/ の中に入れて準備完了。

以下コード。
import org.apache.poi.hssf.usermodel.*;

def myVal=[
["あああ","いいい","ううう"],
["かかか","ききき","くくく"],
["さささ","ししし","すすす"]
]

//新規ワークブック作成
HSSFWorkbook myWb = new HSSFWorkbook();
//新規シート作成
HSSFSheet mySheet = myWb.createSheet();

for(i in 0..<myVal.size){
//新規行作成
HSSFRow myRow = mySheet.createRow(i);
for(j in 0..<myVal[i].size){
//新規セル作成
HSSFCell myCel = myRow.createCell((short)j);
myCel.setEncoding(HSSFCell.ENCODING_UTF_16);
myCel.setCellValue(myVal[i][j]);
}
};

//作成したワークブックを保存する
new File("Test.xls").withOutputStream{myWb.write(it)}

poi.groovy として保存、保存したディレクトリに移動して
groovy poi.groovy
でエクセルファイルができた。簡単。

2009年9月4日金曜日

拡張子を切る

「ファイル名はそのままで拡張子だけ変えたテキストを作りたい」とか、「ファイル名の後ろに"_copy"とつけて別名保存」とかの時に使う。自分コピペ用。
function cutEx(path){
//拡張子を切る
var pathTxt=path, exText="";
var dirCutNo=( (/\//.test(path))? path.lastIndexOf("/") : 0 );
if(/\./.test(path.slice(dirCutNo))){
pathTxt=path.slice(0, path.lastIndexOf("."));
exText=path.slice(path.lastIndexOf("."));
};
return [pathTxt,exText];
};

var filePath="aaa/zasshi.indd";
alert("拡張子: "+cutEx(filePath)[1]+"\nファイルパス: "+cutEx(filePath)[0]);


2010.12.12追記:
この記事で何をしたかったのか思い出し中。
拡張子の前に.のあるファイル名でも、パスより前に.があっても拡張子を取りたくて、あるいは拡張子がなくってもそのパスだけ欲しかったってことか。
今ならこう書く。
function cutEx(path){
return path.split(/(\.[^\.\/]+)$/);
};

var cutPath;
cutPath=cutEx("a.aa/za.sshi.indd")
alert("拡張子: "+cutPath[1]+"\nファイルパス: "+cutPath[0]);

var cutPath;
cutPath=cutEx("/a.aa/zasshi")
alert("拡張子: "+cutPath[1]+"\nファイルパス: "+cutPath[0]);

var cutPath;
cutPath=cutEx("a.aa/za.sshi.txt")
alert("拡張子: "+cutPath[1]+"\nファイルパス: "+cutPath[0]);

2009年8月30日日曜日

Grails-1.1.1で「Simple」wiki を動かしてみる

ちょっと思う所あり、日本Grails/Groovyユーザーグループ(JGGUG)でソース公開している、Grailsで実装したWiki「Simple」を動かしてみました。
環境はMacOSX 10.5.8。

まずあらかじめGrailsの動く環境を整えておきます。現在の最新版、Grails-1.1.1を準備しました。
セットアップはいろいろな人が書いているので省略。

自分のワークディレクトリに移動し、 http://code.google.com/p/gcrnagoya/ からソースコードをSVNで落とします。
$ svn checkout http://gcrnagoya.googlecode.com/svn/trunk/simple simple

simpleフォルダができるので、中の application.properties を確認。
#Do not edit app.grails.* properties, they may change automatically. DO NOT put application configuration in here, it is not the right place!
#Thu May 07 12:43:04 JST 2009
app.version=0.1
app.servlet.version=2.4
app.grails.version=1.0.5
app.name=simple

Grailsの作成バージョンが1.0.5なので、grails upgrade(※あんまり信用されてないコマンドなので注意)を実行します。
$ grails upgrade

もう一度 application.properties の中を確認すると、
#utf-8
#Sat Aug 29 18:13:30 JST 2009
app.version=0.1
app.servlet.version=2.4
app.grails.version=1.1.1
plugins.hibernate=1.1.1
app.name=simple


次に grails-upp/conf/ に BuildConfig.groovy を作成し、以下を記述します。
grails.project.plugins.dir="work_tmp/plugins"
grails.project.work.dir="work_tmp/work"

これを書いておくと、 デフォルトでは ~/.grails/1.1.1/plugins にインストールされるプラグインを、アプリケーションの work_tmp/plugins/ 以下にインストールしてくれます。

plugins フォルダにプラグインのzip(grails-acegi-0.3.zip,grails-feeds-1.4.zip)が入っているのですが、これをさっくり無視して本家リポジトリからプラグインをインストールします。
$ grails install-plugin acegi
$ grails install-plugin feeds


application.properties を確認すると、
#utf-8
#Sat Aug 29 18:25:48 JST 2009
plugins.feeds=1.4
app.version=0.1
plugins.acegi=0.5.2
app.servlet.version=2.4
app.grails.version=1.1.1
plugins.hibernate=1.1.1
app.name=simple

となりました。
アプリケーションにwork_tmpフォルダができて、中の plugins の中に acegi-0.5.2 と feeds-1.4 がインストールされます。
アプリケーション直下の plugins フォルダは捨ててしまいます。

これで、grails-appしてみます。
$ grails run-app
Welcome to Grails 1.1.1 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /opt/grails-1.1.1
----- 省略 -----
create default role & user
Server running. Browse to http://localhost:8080/simple


ブラウザから http://localhost:8080/simple を開くと、


わー。
初期状態だとユーザがひとつもないので、ユーザを新規作成します。


登録すると自動でログインします。
トップページがないので、まずトップページを作成します。



初期状態だと、タイトルに「top」とつけたページがトップページになるようです。それを「index」に変更したいので、
grails-upp/conf/config.groovy の 76行目
simple.contents.toppage="top"

simple.contents.toppage="index"
に変更します。
リロードすると index がトップになるように反映されています。


とてもシンプル。

8/30追記:
ところで、起動コンソールを見ていると、いくつか起動時にエラーが出ています。

2009-08-30 14:07:55,428
[main] ERROR commons.DefaultGrailsControllerClass -
The allowedMethods property in RoleController should be declared static.
The non static version is supported for now but has been
deprecated and may not work in future versions of Grails.
----- 省略 -----

これを解消するために、grails-app/controllers/
  • RegisterController.groovy
  • RequestmapController.groovy
  • RoleController.groovy
  • UserController.groovy
の、
def allowedMethods
となっている部分を
static allowedMethods
に修正します。

参考ページ:Grails-1.0.*からGrails-1.1への〜 Pt.1 - leftovers...

2009年8月22日土曜日

Flash Catalyst を見た話。


今日、うちの会社の会議室にて、FxUGの「Flex勉強会第78回@名古屋」がありました。
自分も一緒に聴講させてもらったのですが、アドビの轟さんがしてくれた、Flash Builder 4 と Flash Catalyst のプレゼンが非常に面白かったので、少しここにも書いておきます。

Flash Catalyst というのは、Illustator や Photoshop で作ったデザインを Flex のビューとして再利用するためのツールです。自分も今回初めて知りました。

イベントレポート「Flash Catalyst / Flash Builder 4 ベータ公開イベント」
Adobe Edge: 2008年12月 Flash Catalyst:PhotoshopデザインがそのままFlexアプリのViewへと変わる!

Webデザインのインターフェースを作る時、今まではデザイナーがIllustratorでカンプを作っても、結局WEB担当者が見た目を似せて作り直したりしていました。せいぜい画像パーツとして切り出す程度。
Flash Catalyst を使うと、IllustratorやPhotoshopのファイルで作り込んだパーツを、そのままFlexのフォームやボタンに変換できる。Flexで作ったWebページや、Airアプリの見た目を、プログラマに頼らずデザイナーが「動き」の部分まで作れるようになるわけです。

今回の轟さんのプレゼンは、Flexアプリケーションを、全くプログラムを書かずに最後まで作る、というコンセプトでした。デザイナーが作ったIllustratorファイルの、普通のパスの四角形で作った部分を選択して右クリックでテキスト入力フォームに変換したり、モーションを追加したりするのを見て、おお、ここまで出来るのかと感嘆。
まあ操作で覚えることは沢山あるにしても、少なくともプログラムは一切書かないし、少し努力すればたいていのデザイナーなら使えるレベルだと思います。

デザイナーが作成したビューをFlexプログラマに渡して、データを読み書きできるようにします。それをやるのが新しい Flash Builder。こちらもデータをドラッグ&ドロップで読み来んでActionScriptのコードを自動生成したりと、面白いデモがてんこもりでした。
でもやっぱり印象的だったのは Catalyst。紙媒体中心の仕事からWEBへの参入は結構壁が厚いと思いますが、Flex、Airのビューという選択肢がひとつ増えた感じ。
どちらにしろ紙媒体の仕事はどんどん減ってるわけだし…

自分はデザインはだめだし、組版方面の話ではないけれど、なんだか最近暗い話ばかりの業界でも面白そうな方向に行けばまだまだ面白い仕事はあるんだ、ということを実感させてくれるようなプレゼンでした。

ps. 他の講演も、会場の雰囲気も活気があって面白い話ばかりでした。
FxUG の皆さんありがとうございました。

2009年8月14日金曜日

perlでJSONを使う

PerlでJSONを作るには、JSONモジュールを使う。
WINDOWSで最新のActivePerlの場合は、JSONモジュールは最初から入っていた。
「Perl Package Manager」から確認できる。


Macの場合は、CPANからインストールする必要がある。
先にCPANのインストール。
参考にしたサイト:
CPAN経由でLinuxにモジュールを組み込む
Heart Beat � Blog Archive � Mac OSXでPerlの環境を整える
MacでCPANを使ってPerlモジュールインストール - nmon
CPANを入れたら、コンソールから
$ sudo cpan
Password:
>install JSON
>exit

これでOK。

使うには、use JSON; を指定して、ハッシュのリファレンスを渡してやる。
#test2.pl
use strict;
use warnings;
use utf8;
use Encode;
use JSON;
binmode STDIN, ':encoding(utf8)';
binmode STDOUT, ':encoding(utf8)';
binmode STDERR, ':encoding(utf8)';

my %hash = (
'チョウ' => {
'アゲハチョウ'=>'Papilio xuthus',
'モンシロチョウ'=>'Pieris rapae',
'ゴマダラチョウ'=>'Hestina persimilis japonica'
},
'コガネムシ' => {
'マメコガネ'=>'Popillia japonica',
'カナブン'=>'Rhomborrhina japonica',
'スジコガネ'=>'Anomala testaceipes'
},
'バッタ' => {
'トノサマバッタ'=>'Locusta migratoria',
'ショウリョウバッタ'=>'Acrida cinerea',
'コバネイナゴ'=>'Oxya yezoensis'
}
);

my $hash_ref = \%hash;
my $hash_json = JSON->new->encode($hash_ref);

print $hash_json;
print "\n";

これで、コンソールから
perl test2.pl
で、
{"チョウ":{"ゴマダラチョウ":"Hestina persimilis japonica","モンシロチョウ":"Pieris rapae","アゲハチョウ":"Papilio xuthus"},"バッタ":{"トノサマバッタ":"Locusta migratoria","コバネイナゴ":"Oxya yezoensis","ショウリョウバッタ":"Acrida cinerea"},"コガネムシ":{"スジコガネ":"Anomala testaceipes","マメコガネ":"Popillia japonica","カナブン":"Rhomborrhina japonica"}}
と表示される。OK。

参考:
[Perl] JSON モジュールの utf8 フラグ周りの仕様 tips 注意点 Kawa.netブログ(川崎有亮)/ウェブリブログ

2009年8月11日火曜日

とりあえず上手くいくperl5.8のエンコード指定テンプレ

正規表現がまともに動いて、文字化けもしない設定がわかってきたのでメモ。
下は簡単な検索置換スクリプトサンプルです。
#!/usr/bin/perl;

use strict;
use warnings;
use utf8; # ソースコード(このスクリプト自体)をUTF8で書くよという宣言
use Encode; # Encode.pm モジュールを使用する宣言

binmode STDIN, ':encoding(utf8)';
binmode STDOUT, ':encoding(utf8)';
binmode STDERR, ':encoding(utf8)';
# 標準出力(コンソールから打ち込む文字、または
# ブラウザから送られてくる文字)のエンコード指定。
# binmode はバイナリモード。
# Windowsのコマンドプロンプトに出すならば ':encoding(cp932)' を使う。

if(@ARGV < 2){
print "Usage: perl test.pl infile outfile\n";
exit;
};

my $INFILE=$ARGV[0];
my $OUTFILE=$ARGV[1];

open my $in, "<:encoding(cp932)", $INFILE or die "$!";
# 読み込み時にかならずファイルのエンコードを指定。
# これで読み込んだ文字に「UTF8フラグ」が立つ。
my @file = <$in>;
close($in);

foreach my $line (@file){
$line =~ s/わらつたよ/うたつたよ/g;
$line =~ s/かぷかぷ/ボェ〜〜〜〜〜と/g;
$line =~ s/クラムボン/ジャイアン/g;
};

open my $out, ">:encoding(utf8)", $OUTFILE or die "$!";
# 書き出す時もかならずエンコードを指定する。
# 「UTF8フラグ」もきちんと取って出力される。
print $out @file;
close($out);


これで、コンソールから
$ perl test.pl infile.txt outfile.txt

を実行すると、WINDOWS shift-jis で書かれた infile.txt
『クラムボンはわらつたよ。』
『クラムボンはかぷかぷわらつたよ。』
『クラムボンは跳てわらつたよ。』
『クラムボンはかぷかぷわらつたよ。』

を読み込んで、UTF8 の outfile.txt
『ジャイアンはうたつたよ。』
『ジャイアンはボェ〜〜〜〜〜とうたつたよ。』
『ジャイアンは跳てうたつたよ。』
『ジャイアンはボェ〜〜〜〜〜とうたつたよ。』
を吐き出します。

結局
  • ソースファイルはUTF8で書き、use utf8を指定
  • 標準出力にbinmodeでエンコードを指定
  • 読み込み、書き出し時にファイルのエンコードを指定

これを守って書けばたいていは上手くいきそうです。
自分用コピペ元として…。

参考:
404 Blog Not Found:perl - Encode 入門
Perl5.8 の UNICODE 対応
WindowsでPerlを使うもんじゃない
Perl: use utf8 と use encoding と \w - えむもじら

2009年8月5日水曜日

JavaScript内に複数行のテキストを書く

JavaScript内に複数行のテキストを書きたいときは、普通はこのように書くと思う。
var text="何が面白くて駝鳥を飼ふのだ。\n"
+ "動物園の四坪半のぬかるみの中では、\n"
+ "脚が大股過ぎるぢやないか。\n"
+ "頸があんまり長過ぎるぢやないか。\n"
+ "雪の降る国にこれでは羽がぼろぼろ過ぎるぢやないか。\n"
+ "腹がへるから堅パンも食ふだらうが、\n"
+ "駝鳥の眼は遠くばかり見てゐるぢやないか。\n"
+ "身も世もない様に燃えてゐるぢやないか。\n"
+ "瑠璃色の風が今にも吹いて来るのを待ちかまへてゐるぢやないか。\n"
+ "あの小さな素朴な頭が無辺大の夢で逆まいてゐるぢやないか。\n"
+ "これはもう駝鳥ぢやないぢやないか。\n"
+ "人間よ、\n"
+ "もう止せ、こんな事は。";

alert(text);
これだと+と\nと""だらけで、いかにも読みにくい。
実は、下のようにも書ける(のだと最近知った)。
var text="何が面白くて駝鳥を飼ふのだ。\
動物園の四坪半のぬかるみの中では、\
脚が大股過ぎるぢやないか。\
頸があんまり長過ぎるぢやないか。\
雪の降る国にこれでは羽がぼろぼろ過ぎるぢやないか。\
腹がへるから堅パンも食ふだらうが、\
駝鳥の眼は遠くばかり見てゐるぢやないか。\
身も世もない様に燃えてゐるぢやないか。\
瑠璃色の風が今にも吹いて来るのを待ちかまへてゐるぢやないか。\
あの小さな素朴な頭が無辺大の夢で逆まいてゐるぢやないか。\
これはもう駝鳥ぢやないぢやないか。\
人間よ、\
もう止せ、こんな事は。";

alert(text);

ちょっとだけ気分が良い。

2009年8月3日月曜日

InDesignでPDFを貼り込むときのオプション


InDesignにPDFを貼り込むスクリプトを作るとき、いつも忘れてしまうのでメモ。
app.pdfPlacePreferences.pdfCrop=PDFCrop.CROP_TRIM;

これはPDFを「ファイルメニュー→配置」で選んだ時に、「読み込みオプションを表示」で出る画面のトリミングオプション。
PDFはEPSとは違い、いろいろなサイズをあらかじめ持っている。
トリミングサイズ、裁ち落としサイズ、メディアサイズetc...

最初にこれを設定しておかないと、スクリプトを実行した時に思わぬ場所でトリミングされてしまう。

オプションの種類は以下のとおり。
  • PDFCrop.CROP_ART
  • PDFCrop.CROP_BLEED
  • PDFCrop.CROP_CONTENT
  • PDFCrop.CROP_MEDIA
  • PDFCrop.CROP_PDF
  • PDFCrop.CROP_TRIM

2009年7月27日月曜日

Illustratorで選択したオブジェクトをグループ

調べるのにすごいかかったのでメモ。

グループするには、新しくgroupItemを作ってその中にmoveする。
var doc=app.activeDocument;
var sels=doc.selection;

var grp=doc.groupItems.add();
for(var i=0;i<sels.length;i++){
var sel=sels[i]
sel.move(grp,ElementPlacement.PLACEATEND);
};

このmoveのオプションを調べるのにすごく苦労した。
結局、IllustratorCS3_JavaScript_Reference.pdf の後ろの方、Scripting Constants に載っていたのだが。ここにあったこと覚えておかないと…。

ElementPlacementで選べるオプションは以下の5種類。
  • ElementPlacement.INSIDE
  • ElementPlacement.PLACEATBEGINNING
  • ElementPlacement.PLACEATEND
  • ElementPlacement.PLACEBEFORE
  • ElementPlacement.PLACEAFTER
選ぶのを間違えると,グループした時にオブジェクトの前後が逆になったりします。

2009年7月21日火曜日

Illustratorで扇形を描くスクリプト。


がんばって書いたので公開します。
上の画像のような感じできれいな扇形を描く
//中心点o, 半径 r, 開始角度a, 終了角度b とすると、

var o=[100,100];
var r=100;
var a=20;
var b=320;

var doc=app.activeDocument;
var pObj=circularSector(doc,o,r,a,b);
pObj.filled = false; // 塗りなし
pObj.stroked = true; // 線あり
pObj.strokeWidth = 3; // 線幅3ポイント

//---------------------処理ここまで--------------------------
function circularSector(doc,o,r,a,b){
//扇形を作成する。ドキュメントdoc、中心点o、半径 r、開始角度a、終了角度b
//角度を出す
var shema=(b-a)%360;
//90度以上なら角度を分割
var pointsLength=Math.ceil(Math.abs(shema/90));
var shema2=shema/(pointsLength);
//いっこいっこの角度を配列に入れる
var angleAry=[];
for(var i=0;i<=pointsLength;i++){
angleAry.push(a+shema2*i)
};

//パスのポイントの配列を作る
var pointsAry=[o];
for(var j=0;j<angleAry.length;j++){
pointsAry.push(trigonometric(o, r, angleAry[j]));
};
$.writeln(pointsAry);

//パスオブジェクトを生成
var pObj = doc.pathItems.add();
pObj.setEntirePath(pointsAry);
//パスを閉じる
pObj.closed=true;

//パスを選択
pObj.selected=true;
//ハンドルをのばす
var d=arcDirection(r, shema2);
for(var k=1;k<pointsAry.length;k++){
if(k<pointsAry.length-1){
pObj.selectedPathPoints[k].rightDirection=
trigonometric(pointsAry[k], d, angleAry[k-1]+90);
};
if(k>1){
pObj.selectedPathPoints[k].leftDirection=
trigonometric(pointsAry[k], d, angleAry[k-1]-90);
};
};
//パスの選択解除
pObj.selected=false;
return pObj;
};

function trigonometric(o, r, theta){
//三角関数。原点o、半径r、角度theta
var oo,x,y;
x=r*Math.sin(radian(theta));
if(/e/.test(x.toString())) x=0;
y=r*Math.cos(radian(theta));
if(/e/.test(y.toString())) y=0;
oo=[o[0]+x,o[1]+y];
return oo;
};

function arcDirection(r, theta){
//円弧のハンドルまでの距離を出す。半径r、角度theta
var k = (Math.tan(radian(theta)/4))*4/3
return r * k;
};

function radian(theta){
//ラジアンを計算する。角度theta
var rad=theta*Math.PI/180;
return rad;
};

2009年6月22日月曜日

Illustrator8.0の詰めをCS以降で検出できるか?

イラレ8で使われていた文字の「詰め」機能は、CS以降では使えない。
イラレ8で作られたドキュメントをCSで開くと、「テキストを更新」するかどうかを求められ、テキストを更新しなければ文字の編集はできない。更新すれば詰め情報は飛んでしまい、文字はみっともなく広がってしまう。その部分は手作業で直すしかない。
Scriptを使っても詰めが使われていたかどうかの情報は取れないし、自分でもほぼあきらめていた。

が、要するに「文字が広がってしまう」のが問題の本質であるわけだから、更新前と更新後で文字のサイズを取って比較し、不自然に広がってしまうようなら「詰め」が使われていた、という事で良いのでは?
そう思ってスクリプトを書いてみた。環境はMac、CS3。
function convertLegacyText(doc){

var itemSizeList={};
for(var i=0;i<doc.pageItems.length;i++){
var tItem=doc.pageItems[i];
if(tItem.constructor.name=="TextFrame"){
var name=rundumStr(8);
//レガシーテキストに名前をつける
tItem.name=name;
//名前とwidth,heightの値を保存
itemSizeList[name]=[tItem.width,tItem.height];
};
};

//テキストを更新
doc.legacyTextItems.convertToNative();

//配列を作る
var list=[];
for(var j=0;j<doc.textFrames.length;j++){
var tItem2=doc.textFrames[j];
//縦組ならば
if(tItem2.orientation==TextOrientation.VERTICAL){
if(tItem2.height>itemSizeList[tItem2.name][1]*1.01){
list.push(tItem2);
};
//横組みならば
}else{
if(tItem2.width>itemSizeList[tItem2.name][0]*1.01){
list.push(tItem2);
};
};
};
return list;
};

//ランダムな文字列を作成する
function rundumStr(num) {
var outtxt = "";
var text = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var textSize = text.length;
for(var i = 0; i < num; i ++ ){
outtxt += text[Math.floor(Math.random()*textSize)];
}
return outtxt;
};

var doc=app.activeDocument;
var list=convertLegacyText(doc);
if(list.length>0){
alert(list.length+"個のテキストフレームの詰めが崩れました。")
};

イラレ8で詰めが使われているファイルを開く時「テキストの更新」をせずに開き、上記スクリプトを実行する。
すると、「convertLegacyText」functionの返り値に、サイズの変化が大きかったテキストフレームのリストを返す。
何か処理をかけるならば、このリストのtextFrameに対し順番に処理をかける。

更新前と更新後でテキストフレームを区別する方法がなかったが、name属性が空だったので、ランダムに名前をつけてやることで解決できた。
あとの処理は考え中……。

2009年6月21日日曜日

ExtendScriptでそのオブジェクトに使えるメソッドを表示する

例えば、Illustratorのドキュメントを開いて,以下を実行
var doc=app.activeDocument;

var docMethods=doc.reflect.methods;
for(var i=0;i<docMethods.length;i++){
$.writeln(docMethods[i]);
};

すると、ESTKのコンソールに表示される。
close
save
saveAs
print
exportFile
activate
importVariables
exportVariables
importCharacterStyles
importParagraphStyles
importPrintPreset
exportPrintPreset
importPDFPreset
exportPDFPreset
imageCapture
undefined

2009年6月3日水曜日

今スクリプトをどこから起動しているか調べる

alert(BridgeTalk.appName);

これを保存して、Photoshopの ファイルメニュー → スクリプト から起動すると

ExtendScript Toolkitから起動すると


※6:21追記:この設定、あまり意味ないかも。
app.nameとの違いは、app.nameだと "ExtendScript Toolkit" と返すけれど
BridgeTalk.appNameは "estoolkit" と返します。

2009年5月26日火曜日

Illustrator Scriptでアラートダイアログを出さないようにする。

Illustrator CS3にて確認。
アラートを出さないようにするには、
app.userInteractionLevel=UserInteractionLevel.DONTDISPLAYALERTS;

アラートを出すようにするには
app.userInteractionLevel=UserInteractionLevel.DISPLAYALERTS;
※アラート回避処理をしたスクリプトには、必ず末尾にアラートを出す処理を書いて元に戻しておくこと。

2009年5月25日月曜日

ExtendScriptでディレクトリを掘り下がる

ディレクトリ降下は再帰処理の基本。
ExtendScriptのコンソールに、あるフォルダ以下にある全てのファイルのパスを表示します。
var folderObj = Folder.selectDialog("フォルダを選択してください");
if(folderObj){
(function(folderObj){
var fileList = folderObj.getFiles();
for(var i=0;i<fileList.length;i++){
if(fileList[i].constructor.name=="Folder"){
arguments.callee(fileList[i]);
}else{
$.writeln(fileList[i].fsName);
};
}
})(folderObj);
};

2009年5月23日土曜日

文字から文字コードを得る、文字コードを文字に戻す

InDesign CS3のテキストフレームの文字からUnicodeを表示し、そのUnicodeを文字に戻します。
//選択テキストフレームの文字
var myChar = app.activeDocument.selection[0].characters[0].contents;
alert("文字:"+myChar);

//charCodeAtで数値を取って16進数で表示
var myCharCode = myChar.charCodeAt(0).toString(16);
alert("文字ユニコード:"+myCharCode);

//文字コードを数値に戻す
var c = parseInt("0x"+myCharCode);
//fromCharCodeで文字に戻す
var myCharFromCode = String.fromCharCode(c);
alert("ユニコード"+myCharCode+"の文字は:"+myCharFromCode);



2009年5月5日火曜日

XAMPP for MacOS X で PEAR を使ったphpが動かない件。

php勉強中。

動かしてたサンプルに PEAR の HTML_QuickForm というライブラリを使ったものがあったが、それを XAMPP の htdocs に入れても動かない。
なのでいろいろ調べた。XAMPP 1.0.1。

その1:PEARのコマンドは、/Applications/XAMPP/xamppfiles/bin/pear にある。これで HTML_QuickForm をインストール。
$ sudo su
Password:********
# /Applications/XAMPP/xamppfiles/bin/pear install HTML_QuickForm

でもやっぱり動かない……。

その2:php.iniの、PEARのパスが間違ってた。
/Applications/XAMPP/etc/php.ini のいちばん最後、
include_path=".:/Applications/xampp/xamppfiles/lib/php"

include_path=".:/Applications/xampp/xamppfiles/lib/php/pear"
にする。これでPEARのライブラリが呼び出せる。

追記0507: php.iniの内容はそのままで、phpのライブラリを読み込んでいるところを
require_once("HTML/QuickForm.php");
から
require_once("pear/HTML/QuickForm.php");
にした方が良い。他で読み込んでるものがあるかも知れないし……。

2009年4月30日木曜日

InDesign Scriptでアラートダイアログを出さないようにする。

ちょっと嵌ったのでメモ。
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;

これをスクリプトの最初に宣言する。
app.scriptPreferences.userInteractionLevelで宣言することができるのは、以下の3種類。

  • UserInteractionLevels.INTERACT_WITH_ALERTS
    警告のダイアログのみ表示する。

  • UserInteractionLevels.INTERACT_WITH_ALL
    全てのダイアログを表示する。

  • UserInteractionLevels.NEVER_INTERACT
    全てのダイアログを表示しない。


使った後は、スクリプトの最後に
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.INTERACT_WITH_ALL;
を書いて元に戻しておかないと、他でアラートを使うスクリプトを走らせた時に、アラートが出なくなってしまうので注意。

ちなみにPhotoshopだと
app.displayDialogs = DialogModes.NO;
を使う。
Illustratorの場合はこちら

replaceの検索結果をさらにreplaceする。

JavaScriptのreplaceの置換文字列のところにfunctionを書けるよ、という話。
replace - MDC

例えば、テキスト内の「1000円」「2000円」といった数字全てにカンマを入れたいという場合
function plusCamma(str){
str = str.replace(/([0-9]+?)(?=(?:[0-9]{3})+$)/g , '$1,');
return str;
};

var text="2009年お正月セール、当店平日価格23000円の品、特価9999円!";
text=text.replace(/[0-9]+(?=円)/g, plusCamma);
alert(text);
と書ける。


置換文字列のfunctionに引数の()は入れない。
でもそれだと、検索結果全体にしか変換をかけられない。
キャプチャした$1、$2なんかを利用したい時はどうすればいいんだろう?

そう思って調べてみた。
ExtendScript Toolkitで、$.writelnを使ってコンソールに出してみる。
function test(str){
var ary=[];
for(var i=0;i<arguments.length;i++) ary.push(arguments[i])
$.writeln("数:"+arguments.length+"、引数:"+ary.join(" || "));
return "<変換済み>";
};

var text="2009年お正月セール、当店平日価格23000円の品、特価9999円!";
text=text.replace(/([0-9]+)(円)/g, test);
$.writeln(text);

結果はこんな風。
数:5、引数:23000円 || 23000 || 円 || 18 || 2009年お正月セール、当店平日価格23000円の品、特価9999円!
数:5、引数:9999円 || 9999 || 円 || 29 || 2009年お正月セール、当店平日価格<変換済み>の品、特価9999円!
2009年お正月セール、当店平日価格<変換済み>の品、特価<変換済み>!
undefined


argumentsの中に順番に、$0、$1、$2、検索された文字の位置、置換される前のテキスト全体が入って来てる。
なので$1を使いたい場合は、arguments[1]を拾えば良い。

function plusCamma2(str){
var str=arguments[1];
str = str.replace(/([0-9]+?)(?=(?:[0-9]{3})+$)/g , '$1,')
+arguments[2];
return str;
};
var text="2009年お正月セール、当店平日価格23000円の品、特価9999円!";
text=text.replace(/([0-9]+)(円)/g, plusCamma2);
alert(text);
のように書いても良い。という事。

2009年4月24日金曜日

InDesignのスクリプトをキーボードショートカットで使う。

今日初めて知ったのですが、InDesignのスクリプトにキーボードショートカットを割り当てられるんですね。
「編集→キーボードショートカット」を開いてみると…

ほら、インストールしてあるスクリプトがずらーーーっと出る。
よく使うスクリプトにショートカットを割り当てて、バリバリ仕事をこなしましょう!

2009年4月19日日曜日

InDesignCS4でライブラリに登録すると画像のタグが消える?

メモ、というかヘルプ!
InDesign CS4でライブラリに登録した画像フレームの、XMLタグが飛んでしまう…。

具体的には次の通り。InDesign、バージョン6.0.1。
まず、たくさんの画像フレームを作り、それぞれに別々のタグを設定する。

それをライブラリに登録する。

元のドキュメントに戻すと…

左1行目、下4つの画像からタグが飛んでしまっている!
抜ける部分は同じライブラリのアセットなら同じだけれど、何度も登録するとその度にいくつかタグが飛ぶ。
これはXMLで自動組してる会社にとっては死活問題なのでは…???

追記0420:コメントにて、せうぞー様より再現しないというご報告をいただきました。個人の設定に依存する現象かも知れません。

追記0424:すいません、忙しくて追記遅れました。
匿名様より再現したとのコメントを頂きました。
原因はわかりませんが自分だけの現象ではないようですので、ブログのエントリは残しておこうと思います。何かわかりましたらまたエントリを書くつもりです。

追記20100108:Adobeのいわもとさんが自ブログでCS4の問題として紹介されてました。
ライブラリに登録するとグラフィックフレームからタグが無くなる - いわもとぶろぐ

追記20110205:一度、解消したというエントリを書いたのですが、間違っていました。
その後検証しないまま、そちらのエントリに打ち消し線と注意書きをして残していたのですが、誤解されるかもしれないのでエントリ削除しました。

2009年4月16日木曜日

VirtualBox上のCentOSに接続するにはブリッジにすれば良いのでした。

前の記事を読んだ会社の先輩(php師匠)から一言
「kanemuの記事わけわかんない。なんでブリッジでやんないの?」
と言われる。???

先輩、僕が使ってたMacBookProのVirtualBoxを立ち上げさせて、キーを叩きはじめる。

1. CentOSのバーチャルマシンを選択し、設定→ネットワーク→設定 から
割当てを「ブリッジ ネットワーク」にする。

2. マシンを起動し、rootでログイン。
# ifconfig
を入力してIPアドレスを確認。「192.168.2.4」になっていた。

3. Macのターミナルから
$ ssh <ユーザ名>@192.168.2.4
でログインして普通に入れた。
http://192.168.2.4/を開けば、apacheへも難なく繋がる…。

?????
「仮想マシンへのログインで苦労した事ないからさ、
 むしろこの方法でログインできるのを見つけてくる方が関心するわ」
だそうです。
というわけで、無駄な苦労をしていたというオチでした。はぁ。

2009年4月13日月曜日

VirtualBoxにCentOS 5.3をインストールして、ローカルからsshでアクセスできるようになるまで

MacBook Pro、MacOS X 10.5.6のVirtualBox2.2にCentOS 5.3をインストールしました。
走り書きなので完全にメモ用です。いずれ分かりやすくまとめ直すかも……。

VirtualBox
CentOS

ふつうにDVDイメージをダウンロードするとかなり重いので、BitTrrentクライアントをインストール。
自分はTransmissionというのを入れた。
Transmission

http://isoredirect.centos.org/centos/5/isos/x86_64/
から適当なミラーに入り、CentOS-5.3-x86_64-bin-DVD.torrentをダウンロード。
それをTransmissionのウィンドウにドラッグ。addを押すとダウンロードが始まる。
たぶん普通にDVDイメージを落とすより速い。

VirtualBoxの新規仮想マシンを作成。
オペレーティングシステムはLinux、バージョンはLinux 2.6にする。(RedHatのがいいかも)
設定→ストレージ→CD/DVD-ROM を開き、「CD/DVDドライブのマウント」にチェック。
ISOイメージファイルを選び、先にダウンロードしたDVDイメージを選択する。
これでDVDを焼かずにインストールできる。

ITPro/インストール完全ガイド:CentOS 5.3
CentOSで自宅サーバー構築
等を見ながらインストール。
ブログでは、林檎生活100: CentOS 5でつくるPHP開発サーバ.#1,#2,#3,#4,#5
がとても役に立った。
デフォルトでも良いけれど、たぶん開発環境(gcc)だけは入れておいた方が良い。

httpを起動する
# yum -y install httpd
# /sbin/chkconfig httpd on
# /etc/rc.d/init.d/httpd start

ドキュメントディレクトリのオーナーを変更
# chown -R <ユーザ名> /var/www/html

この状態でゲストOSから
http://localhost/
でcentOSのapacheページが見られる。

ホストOS側からssh接続できるようにする。
VirtualBox on Mac OS X で、ゲスト OS に ssh/http アクセスするまで - 腹八分目
を参考にしました。
ゲストのCentOSを終了して、Macのターミナルから次を設定。
$ VBoxManage setextradata "CentOS" "VBoxInternal/Devices/
e1000/0/LUN#0/Config/guestssh/Protocol" TCP
$ VBoxManage setextradata "CentOS" "VBoxInternal/Devices/
e1000/0/LUN#0/Config/guestssh/GuestPort" 22
$ VBoxManage setextradata "CentOS" "VBoxInternal/Devices/
e1000/0/LUN#0/Config/guestssh/HostPort" 50022
$ VBoxManage setextradata "CentOS" "VBoxInternal/Devices/
e1000/0/LUN#0/Config/guesthttp/Protocol" TCP
$ VBoxManage setextradata "CentOS" "VBoxInternal/Devices/
e1000/0/LUN#0/Config/guesthttp/GuestPort" 80
$ VBoxManage setextradata "CentOS" "VBoxInternal/Devices/
e1000/0/LUN#0/Config/guesthttp/HostPort" 50080

ここでハマったのが、「e1000」の部分。ここはVirtualBox側から見るネットワークアダプタの名前。参考ブログ側だと「pcnet」となっているけれど、自分の環境ではアダプタタイプがIntel PRO/1000 MT Desktop (82540EM) となっていて、この場合は「e1000」と書かなきゃいけない。
「"CentOS"」は自分の仮想マシンの名前。
※もっと簡単な方法もあります!

これで、Macのターミナルからssh接続で
$ ssh -p 50022 -l <ユーザ名>localhost

でアクセスできる。

CyberduckでファイルをUPする場合は、
プロトコル:SFTP
サーバ:localhost
ポート:50022
で接続できる。

http://localhost:50080/
をブラウザで開けば、さっきのapacheのページが…開かない。
これはCentOS側のファイヤーウォールの問題だった。CentOSのシステム→管理→セキュリティレベルとファイヤーウォールの設定 から、ファイヤーウォールを無効にする。

ログイン時にxWindowを立ち上げないようにする。
viで/etc/inittab を編集。
# vi /etc/inittab
id:5:initdefault の行を、id:3:initdefaultにして保存。
xWindowを立ち上げたいときは、
$ startx
を入力する。
仮想マシンの電源を落とす。
# shutdown -h now

2009年2月26日木曜日

フレームグリッドをJPEGで書き出すと上下に余白ができる?

InDesign CS3,CS4にて現象を確認。

フレームグリッドを作り、色をつけてJPEG書き出しします。


出たJPEGが、これ。
わかりにくければ、ダウンロードして開いてみて下さい。
???
ちなみに、タテ組みで出しても、上下に余白ができます。

テキストフレーム、画像フレームならば余白はできません。
これはどういう現象?謎だ……。

2009年2月23日月曜日

Illustratorのページ上のアイテムをeachする

Object.prototype.eachItems=function(funcs){
//Document.pageItemsの時はgroup内のものを除外
var items;
if(this.hasOwnProperty("pageItems")){
items=[];
var ps=this.pageItems;
for(var i=0;i<ps.length;i++){
if(ps[i].parent.typename!="GroupItem") items.push(ps[i]);
}
}else if(this instanceof Array){
items=this;
}else{
items=[this];
};
//引数をArrayにする
var fn=[];
for(var f=0;f<arguments.length;f++){
fn.push(arguments[f]);
};
//内部関数
(function(items,fn){
for(var i=0;i<items.length;i++){
var item=items[i];
if(item.hasOwnProperty("pageItems")){
var groupItems=item.pageItems;
arguments.callee(groupItems,fn);
}else{
for(var j=0;j<fn.length;j++){
fn[j](item);
}
}
}
})(items,fn);
};

function hoge(obj){
if(obj.constructor.name=="TextFrame"){
obj.contents="テキストの中身を入れ替えます";
};
};
function huga(obj){
$.writeln(obj.typename);
};

app.activeDocument.eachItems(
hoge,huga
);
Illustrator CS3にて動作確認。
この間から昔のgroopLoopを引っ張ってごにょごにょやってて、やっと書けたので公開。

Illustratorの図版修正はほぼ自分の仕事になっています。やっててすごくめんどくさい繰り返し作業(全角数字を半角にツメるとか、文字を置換するとか…)を見つけたとき、スクリプトを書いて処理したいなーと思うのですが、
時間さえかければたいていのスクリプトは書ける自信はあるけれど、締め切りは常に迫っていて、スクリプトを書くのに1時間かけていたら意味がない。悶々としながらひたすら作業をくり返すわけです。
単機能の書き捨てスクリプトなら、10分で書けるようにならなきゃ!

方針としては、pageItemごと、TextFlameとかPathItemとかの処理を書いてfunctionにする。それだけなら大した時間はかからないので、その上でeachItemsの引数にfunctionを渡してやる、functionはいくつ渡してもいい。
それで、そういう小さいコードをたくさん書いて保持しておけば…

2009年2月21日土曜日

evalを使わずにテキストになった配列をArrayに戻す

new Function を使います。
var aryText="[\"チョウ\",\"ガ\",\"トンボ\",\"ハチ\",\"カブトムシ\"]";
alert(aryText[4]); // <- ウ

new Function("return ary="+aryText)();
alert(ary.length); // <- 5
alert(ary[4]); // <- カブトムシ

2009年2月17日火曜日

pageItemsとallPageItems

大間違いをしてたようです。お詫びエントリ。
昨年9月に描いたグループをさかのぼれで、InDesignのグループ見つけたらさらにその中を調べる、という処理を書きましたが…InDesignでallPageItemsを使うのであれば、そんな処理はいらないようです。

実験。InDesignはCS3。
まず新規ドキュメントに、てきとうにいろいろなアイテムを10個作り、それの中から3つグループを作ります。


それで、さらにその中の2つのグループをグループします。


準備完了。allPageItemsの中を調べてみます。
var objs=app.activeDocument.allPageItems;
alert(objs+"\n"+objs.length);


14個返ってきます…
allPageItemsは、そのドキュメントのGroupを含めた、全てのアイテムの配列を返しているようです。なのでグループを含めない数は、constructor.nameがGroup以外のものを数えればOK。

ところで、pageItemsだったらどうでしょう?
var objs=app.activeDocument.pageItems;
alert(objs+"\n"+objs.length);

???
pageItemsだと個数は4。ドキュメントの上のアイテムを(グループを1つとして)返してるようなんですが、配列じゃなくpageItemsってオブジェクトを返してくる。
もうちょっと調べてみます。ここからはalertなしで。
var objs=app.activeDocument.pageItems;
$.writeln(objs.properties.toSource());
とやろうとしたら、「エラー: オブジェクトはプロパティまたはメソッド 'properties' をサポートしていません」と出るので、それではと
var objs=app.activeDocument.pageItems;
for (var i in objs) {
$.write(i+":");
try{
$.writeln(objs[i])
}catch(e){
$.writeln(e.toString())
}
};
だと
length:4
しか返らない。
ar objs=app.activeDocument.pageItems;
for (var j=0;j<objs.length;j++) {
$.writeln(objs[j].getElements ());
};
ならば
[object PageItem]
[object PageItem]
[object PageItem]
[object PageItem]
全部「PageItem」なんですよね。constructor.nameで分岐できない…。
オブジェクトモデルを調べてみると、pageItemのところにgetElementsというメソッドがありました。
var objs=app.activeDocument.pageItems;
for (var j=0;j<objs.length;j++) {
$.writeln(objs[j].getElements());
};
[object Group]
[object Rectangle]
[object Group]
[object TextFrame]
うん、OK。

なので、グループをさかのぼれで書いたスクリプトをやるのなら、次のようになります。
#target "InDesign"
selObjs=app.activeDocument.selection;
//↓toolkitのJavaScriptコンソールに表示するよ
$.writeln ("------Groupを1コとするなら------"+selObjs.length);

var count=0;
groupLoop=function(objs){
for(var i=0;i<objs.length;i++){
//↓getElements()は配列で来るみたい、constructorがArrayだった
var obj=objs[i].getElements()[0];
if(obj.constructor.name=="Group"){
var groupObjs=obj.pageItems;
//↓groupRoop(groupObjs);でいいけど一応
arguments.callee(groupObjs);
}else{
//↓ここの処理を書き換えれば、処理が変わる!
count++;
}
}
return count;
}

groupLoop(selObjs);
$.writeln ("------Groupの中を数えるなら------"+count);
#target "InDesign"
selObjs=app.activeDocument.selection;

groupLoop=function(objs){
for(var i=0;i<objs.length;i++){
var obj=objs[i].getElements()[0];
if(obj.constructor.name=="Group"){
var groupObjs=obj.pageItems;
arguments.callee(groupObjs);
}else{
if(obj.constructor.name=="TextFrame"){
obj.parentStory.contents="ほげ";
}
}
}
};

groupLoop(selObjs);

以上です。すみませんでしたm(_ _)m

2009年2月4日水曜日

page2009に

うちの社長開発リーダーが行っています。
(株)ニューキャスト、展示ホールD、52番ブースです。
よろしければ寄っていって下さい。

2009年1月28日水曜日

スクリプトでタグ付きフレームの画像を削除するとタグが抜ける?


InDesignCS3です。こんな感じに、タグ付フレームに入った画像があったとします。
試しに、ひとつ画像を選択して、
var selObj=app.activeDocument.selection[0];
selObj.allGraphics[0].remove();

すると、画像と一緒にXMLのタグも画像フレームから外れてしまいます。

あれー…??

仕方がないんで、タグ部分をいったん退避してから削除して、フレームにもう一度結びつけ直しました。
var selObj=app.activeDocument.selection[0];
//タグを退避させる
var tag=selObj.associatedXMLElement;
//タグから画像のリンクを削除
tag.xmlAttributes[0].remove();
//画像を削除
selObj.allGraphics[0].remove();
//フレームにタグを結び直す
selObj.placeXML(tag);
//テキストフレームになってしまうので、画像フレームに戻す
selObj.contentType=ContentType.GRAPHIC_TYPE;

これで、とりあえずフレームにタグを残す事はできました。

ただ、これはもっと上手い方法があるような気が…
こういう場合はどこをどう削除すればいいんでしょう?

2009年1月18日日曜日

Adobe Creative Suite 4 のインストールが「準備中」で止まる

かなり稀なケースだと思うのですが、どこかで同じトラブルが起こった時のために…。

会社でAdobe Creative Suite 4 Design Standardを買ってもらったので、早速社用Macにインストールしようとした。ところが、インストーラを立ち上げて、シリアル入れて、規約に同意して、簡易インストールで、と来た所で「インストール」をクリックしても『インストールを準備しています』の表示で止まってしまい、そこから先に進まない。
Adobeサポートのインストール時におけるトラブルシューティング等を読みながら、セーフブート起動やアクセス権の修復やいろいろやってみたが、どうやっても先に進まない。

あらかた思いつく手を全て試した後で、画面が止まる理由を考えた。自分、なにかよけいな事したか?
あっ、と思い出した。機能を確かめようと思って、先にExtendScript Toolkit CS4だけを落としてインストールしてあったのだ。急いでそれを抜いてもう一度試したら、無事にインストールできた。

もしCS4購入前に体験版やら何やらを試していたならば、先にそれらをアンインストールしておいた方がいいかも知れません。以上。