Hatena::ブログ(Diary)

名もないテクノ手 このページをアンテナに追加 RSSフィード

EPUB版『InDesign者のための正規表現入門』

InDesignのTips一覧

2008-10-24

[][][][][]InDesign上で日本語校正支援「proofreader」

なにをするスクリプトか?

InDesign上のテキストの日本語校正支援をします。「ら抜き言葉」や「誤変換」「固有名詞」などの間違いを注釈で指摘します。エンジンには「Yahoo! 校正支援Webサービス」を利用しています。校正精度はYahoo!のエンジンに依存します。

簡単な説明は下記のムービーをご覧ください。

D

高解像度版movie(2.7MB)はこちら。


動作環境

このスクリプトが正常に動作する環境は以下の通りです。Windows環境でも動作する可能性がありますが、動作確認はしていません。テスト歓迎。

  • Mac Pro Quad 3GHz
  • Mac OS X 10.5.5
  • InDesign CS3_J(5.0.4)
  • インターネット接続環境
  • Yahoo!アプリケーションID(無料)が必要です。下記サイトから取得できます。

校正支援Webサービス


ダウンロード

http://www.seuzo.jp/st/scripts_InDesignCS3/index.html#proofreader

こちらからのパッケージ版では、Yahoo!アプリケーションIDは必要ありません。


インストール

スクリプト本体(proofreader.jsx)を

/Applications/Adobe InDesign CS3/Scripts/Scripts Panel/

または

~/Library/Preferences/Adobe InDesign/Version 5.0-J/Scripts/Scripts Panel/

にコピーしてください。エイリアスを入れておくだけでもかまいません。スクリプトパレットから使用します。


使用方法

  1. 「ウインドウ」メニューから「スクリプティング」ー「スクリプト」を選択し、スクリプトパレットを出します。
  2. 校正したいテキストを選択します。
  3. スクリプトパレットから、スクリプト「proofreader.jsx」をダブルクリックします。
    • 初めて起動する場合のみ、Yahoo!アプリケーションIDを入力するダイアログが開きます。取得したIDを入力してください。IDはスクリプトと同じ階層にある「yahooID.txt」に記録され、次回の起動以降に使用されます。
  4. 設定ダイアログが出現します。校正すべき項目をチェックしてください。
  5. 処理が終わるとダイアログが出現して、何カ所校正があったかレポートします。
  6. 注釈パレットで校正内容を確認してください。

  • 【Tips】コードの18行目付近にある下記の行にYahoo!アプリケーションIDを直接書き込むと、ファイルアクセスがなくなり、若干早く処理できるようになります。
YAHOO_ID = "ここに入力するのだ";
  • 校正にまちがいを見つけたら...

Yahoo! JAPANへ校正結果の間違いを報告する



既知の不具合、またはToDo、あるいは仕様

  • 校正後の単語に対して、2重のチェックはできません。たとえば、「出鱈目」という単語は「当て字」でもあり、「鱈」が表外漢字でもあります。しかし、指摘されるのは最初の「当て字」のみです。必要ならば、訂正後にもう一度チェックをしなおしてみてください。
  • Yahoo!サーバーがエラーを返したとき、その内容を精査して見合った警告をしたいけれど、どんな時にどんなエラーを返すのか、テスト不足。<-なんだかよくわからないエラーが出た人は、とりあえずコメントを付けてください。
  • 1行が数千字とかの文章だと、おそらくエラーになります。やったことないですけれど。
  • こういうのって本来はInCopy上で動くと都合がいいのかもしれない。だけど、InCopy持っていないしなぁ。短期雇用していただけるなら、開発してもOKです。

免責事項

  • 変換品質は、「Yahoo! 校正支援Webサービス」に依存します。正確な校正品質を保証するものではありません。
  • このツールを使用する上でデータの破損などのあらゆる不具合・不利益については一切の責任を負いかねますのでご了解ください。
  • このツールはすべてのMacintoshMac OS上で動作をするという確認をとっていませんし、事実上出来ません。したがって、動作を保証するものではありません。

ライセンス

GNU GPLv3

http://sourceforge.jp/projects/opensource/wiki/licenses%252FGNU_General_Public_License_version_3.0


履歴

  • 2008-10-24 ver.0.1 とりあえず版
  • 2008-10-25 ver.0.2 ネットワークに繋がっていなかったとき、エラーで終了するようにした。最初に設定ダイアログを出して、指摘範囲を指定できるようにした。

ソースコード

ブログの仕様やブラウザ環境などにより、「\」が正しくないことがあります。エディタなどで調整してください。

/*
proofreader.jsx
(c)2008 www.seuzo.jp
InDesign内で文字校正をします。
Yahoo!デベロッパーネットワークのアプリケーションIDが必要です。
http://developer.yahoo.co.jp/jlp/KouseiService/V1/kousei.html

2008-10-24	ver.0.1	とりあえず
2008-10-25	ver.0.2	ネットワークに繋がっていなかったとき、エラーで終了するようにした。最初に設定ダイアログを出して、指摘範囲を指定できるようにした。
*/

#target "InDesign-5.0"

////////////////////////////////////////////設定
const YAHOO_DOMAIN = "jlp.yahooapis.jp";
const YAHOO_PATH = "/KouseiService/V1/kousei?";
//Yahoo!アプリケーションIDの取得
YAHOO_ID = "";//ここにアプリケーションIDを直接入力すれば、ファイルを探さない。
const DISPLAY_DIALOG = true;//設定ダイアログを表示するかどうか


////////////////////////////////////////////エラー処理 
function myerror(mess) { 
  if (arguments.length > 0) { alert(mess); }
  exit();
}

////////////////////////////////////////////ファイルを読み込んで文字列を返す
function read_file(my_file) {
	var tmp_str = "";
	if (my_file.reflect.name == "String") {my_file = new File(my_file)};//ファイルパス文字列だった
	if (fh = my_file.open("r")) {//ファイルを読み込みモードで開く
		tmp_str = my_file.read();
		my_file.close();
	}
	return tmp_str;
}

////////////////////////////////////////////データをファイルに書き込む 
function write_file(my_write_file_path, my_data) {
	var my_write_file_path;
	var my_file_obj = new File(my_write_file_path);
	//if (!(my_file_obj.exists)) {myerror("ファイルがありません\n" + my_write_file_path)};
	if(my_file_obj.open("w")) {
		my_file_obj.write(my_data);
		my_file_obj.close();
	} else {
		myerror("ファイルが開けません\n" + my_write_file_path);
	}
}

////////////////////////////////////////////yahooID設定の読み書き
function get_yahoo_ID() {
	var yahooID_file = app.activeScript.parent + "/yahooID.txt";
	var yahoo_ID = read_file(yahooID_file);
	if ((yahoo_ID == null) || (yahoo_ID == "")) {
		yahoo_ID = prompt("Yahoo!アプリケーションIDを入力してください。\n持っていない場合は下記URLから取得してください(無料)\nhttp://developer.yahoo.co.jp/jlp/KouseiService/V1/kousei.html", "");
		if ((yahoo_ID == null) || (yahoo_ID == "")) {myerror("キャンセルしました")}
		write_file(yahooID_file, yahoo_ID);
	}
	return yahoo_ID
}

////////////////ダイアログ
function show_dialog(){
	var my_dialog = app.dialogs.add({name:"日本語校正支援:proofreader", canCancel:true});
	with(my_dialog) {
		with(dialogColumns.add()) {
			// プロンプト
			staticTexts.add({staticLabel:"選択行の校正を実行して、注釈を添付します。"});
			with (borderPanels.add()) {
				with(dialogRows.add()){staticTexts.add({staticLabel:"▼レベル1 - 基本:表記の間違いや不適切な表現に関する指摘"});}
				with(dialogRows.add()){var check_01 = checkboxControls.add({staticLabel:"誤字・誤変換     例)人事異同→人事異動", checkedState:true});}
				with(dialogRows.add()){var check_02 = checkboxControls.add({staticLabel:"言葉の誤用      例)煙に巻く→けむに巻く", checkedState:true});}
				with(dialogRows.add()){var check_03 = checkboxControls.add({staticLabel:"使用注意       例)外人墓地→外国人墓地", checkedState:true});}
				with(dialogRows.add()){var check_04 = checkboxControls.add({staticLabel:"不快語        例)がんをつける→にらむ", checkedState:true});}
				with(dialogRows.add()){var check_05 = checkboxControls.add({staticLabel:"機種依存・拡張文字  例)○付き数字、一部の旧字体など", checkedState:true});}
				with(dialogRows.add()){var check_06 = checkboxControls.add({staticLabel:"外国地名表記の間違い 例)モルジブ→モルディブ", checkedState:true});}
				with(dialogRows.add()){var check_07 = checkboxControls.add({staticLabel:"固有名詞表記の間違い 例)ヤフーブログ→Yahoo!ブログ", checkedState:true});}
				with(dialogRows.add()){var check_08 = checkboxControls.add({staticLabel:"人名表記の間違い   例)ベートーヴェン→ベートーベン", checkedState:true});}
				with(dialogRows.add()){var check_09 = checkboxControls.add({staticLabel:"ら抜き言葉      例)食べれる→食べられる", checkedState:true});}
			}
			with (borderPanels.add()) {
				with(dialogRows.add()){staticTexts.add({staticLabel:"▼レベル2 - 難読:わかりやすい表記にするための指摘"});}
				with(dialogRows.add()){var check_10 = checkboxControls.add({staticLabel:"当て字        例)出鱈目、振り仮名", checkedState:true});}
				with(dialogRows.add()){var check_11 = checkboxControls.add({staticLabel:"表外漢字あり     例)灯籠→灯●", checkedState:true});}
				with(dialogRows.add()){var check_12 = checkboxControls.add({staticLabel:"用字         例)曖昧→あいまい", checkedState:true});}
				with(dialogRows.add()){var check_13 = checkboxControls.add({staticLabel:"商標など用語言い換え 例)セロテープ→セロハンテープ", checkedState:true});}
			}
			with (borderPanels.add()) {
				with(dialogRows.add()){staticTexts.add({staticLabel:"▼レベル3 - 品質:文章をよりよくするための指摘"});}
				with(dialogRows.add()){var check_14 = checkboxControls.add({staticLabel:"二重否定       例)聞かなくはない", checkedState:true});}
				with(dialogRows.add()){var check_15 = checkboxControls.add({staticLabel:"助詞不足の可能性あり 例)学校行く", checkedState:true});}
				with(dialogRows.add()){var check_16 = checkboxControls.add({staticLabel:"冗長表現       例)ことができます", checkedState:true});}
				with(dialogRows.add()){var check_17 = checkboxControls.add({staticLabel:"略語         例)ADSL→非対称デジタル加入者線(ADSL)", checkedState:true});}
			}
			with (borderPanels.add()) {
				with(dialogRows.add()){staticTexts.add({staticLabel:"           (c)2008 市川せうぞー http://www.seuzo.jp/"});}
			}
		}
	}


	if (my_dialog.show() == true) {
		check_01 = check_01.checkedState;
		check_02 = check_02.checkedState;
		check_03 = check_03.checkedState;
		check_04 = check_04.checkedState;
		check_05 = check_05.checkedState;
		check_06 = check_06.checkedState;
		check_07 = check_07.checkedState;
		check_08 = check_08.checkedState;
		check_09 = check_09.checkedState;
		check_10 = check_10.checkedState;
		check_11 = check_11.checkedState;
		check_12 = check_12.checkedState;
		check_13 = check_13.checkedState;
		check_14 = check_14.checkedState;
		check_15 = check_15.checkedState;
		check_16 = check_16.checkedState;
		check_17 = check_17.checkedState;
		//正常にダイアログを片付ける
		my_dialog.destroy();
		return  [false, check_01, check_02, check_03, check_04, check_05, check_06, check_07, check_08, check_09, check_10, check_11, check_12, check_13, check_14, check_15, check_16, check_17];
	} else {
		// ユーザが「キャンセル」をクリックしたので、メモリからダイアログボックスを削除
		my_dialog.destroy();
		myerror();
	}
}

////////////////////////////////////////////yahooへのリクエストと返事
function get_yahoo_respons(post_str) {
	var post_str;
	var reply = "";//サーバーからの返事
	var conn = new Socket;
	if (conn.open ( YAHOO_DOMAIN + ':80', 'UTF-8' ) ) {
		conn.write ('GET ' + YAHOO_PATH +'appid=' + YAHOO_ID + NO_FILTER + '&sentence=' + post_str + " HTTP/1.0\n"
			+ 'Host: ' + YAHOO_DOMAIN + "\n"
			+ 'User-Agent: ' + 'InDesign/5.0.3 ' +'(Macintosh; U; Intel Mac OS X 10_5_5; ja-jp)' + "\n"
            + "\n");
		reply = conn.read(999999);
        conn.close();
	} else {
		myerror("インターネットに接続されていません。");
	}
	return reply;
}



////////////////////////////////////////////実行
//InDesignで選択しているもののチェック
if (app.documents.length == 0) {myerror("ドキュメントが開かれていません")}
var my_doc = app.activeDocument;
if (my_doc.selection.length == 0) {myerror("テキストを選択してください")}
var my_selection = my_doc.selection[0];
var my_class =my_selection.constructor.name;
my_class = "Text, TextColumn, Story, Paragraph, Line, Word, Character, TextStyleRange".match(my_class);
if (my_class == null) {myerror("テキストを選択してください")}
var my_paragraphs = my_selection.paragraphs;//選択している段落

if (YAHOO_ID == "") {YAHOO_ID = get_yahoo_ID()}//Yahoo!アプリケーションIDの読み込み

//ダイアログ処理
NO_FILTER = "";//指摘除外フィルタ(指摘番号をコンマで区切って指定。詳細は、http://developer.yahoo.co.jp/jlp/KouseiService/V1/kousei.html)
if (DISPLAY_DIALOG) {
	var ans_dialog = show_dialog();
	var no_filter_count = 1;//除外数のカウント
	for (var i = 1; i < ans_dialog.length; i++) {//0番地はダミー
		if (ans_dialog[i] == false) {
			NO_FILTER += "," + i;//フィルタ番号を加算
			no_filter_count++;
		}
	}
	if (ans_dialog.length == no_filter_count) {myerror("ダイアログのすべてのチェックが外れています")}
	if (NO_FILTER != "") {NO_FILTER = NO_FILTER.replace (/^,/, '&no_filter=')}
}

//各段落の処理
var my_regex = new RegExp(/^[ \s\r\n]*$/);//空行をみつけるための正規表現
var my_counter = 0;//かうんたっく
for(var i = (my_paragraphs.length -1); i >= 0; i--) {//段落を後ろから処理
	var my_contents = my_paragraphs[i].contents;
	if (my_regex.test(my_contents)){continue;}//空行なら次の段落へ
	var post_str = encodeURI(my_contents);//エンコード
	var my_reply = get_yahoo_respons(post_str);//Yahooへの問い合わせ
	my_reply = my_reply.split("\n\n", 2)[1];//レスポンスヘッダの削除
	
	var my_xml = new XML(my_reply);//XMLオブジェクトの生成
	var my_ns_uri = my_xml.namespace();
	var my_ns = new Namespace(my_ns_uri);
	setDefaultXMLNamespace(my_ns);//デフォルトネームスペースを設定する
	if (my_xml.localName().toString() == "Error"){//エラー要素があったら中止
		myerror("サーバーがエラーを返しました:" + my_xml.xpath("/Error/Message")[0].toString());
	}
	for( var ii = (my_xml.Result.length() - 1); ii >= 0; ii--) {//my_xmlのエレメントを後ろから処理
		var my_StartPos = parseInt(my_xml.Result[ii].StartPos[0].toString());//スタートポイント、文字位置
		var my_Surface = "【誤】" + my_xml.Result[ii].Surface[0].toString() + "\n";
		var my_ShitekiWord = "【正】" + my_xml.Result[ii].ShitekiWord[0].toString() + "\n";
		var my_ShitekiInfo = "<" + my_xml.Result[ii].ShitekiInfo[0].toString() + ">\n";
		
		var my_note = my_paragraphs[i].insertionPoints[my_StartPos].notes.add();//注釈の作成
		my_note.texts[0].contents = my_ShitekiInfo + my_Surface + my_ShitekiWord;//注釈の中身を書き換え
		my_counter++;
	}
}
myerror("校正の結果:\n" + my_counter + "箇所の注釈をつけました");

参考・関連サイト

標準XML完全解説〈上〉

改訂版 標準XML完全解説(下)

校正支援Webサービス

Socketオブジェクトで遊ぶ。いや遊ばれる -- ディザInDesignブログ

選択したテキストフレーム内の漢字に対してルビをふる -- ディザInDesignブログ

InDesignでSocketクラスの上位クラスを作りHTTPでアクセス -- CLの日記

AJAX and scripting Web services with E4X, Part 1 -- IBM

JavaScript を強化した E4X -- IBM

Processing XML with E4X -- mozilla.org

ふりがな(ルビ)を追加 -- seuzo.jp

注釈(Note)の取り扱い - 名もないテクノ手

WebサイトのコンテンツをInDesignに取り込む - 名もないテクノ手

テキストにふりがな(ルビ)を付加する。 - 名もないテクノ手

XMLオブジェクトの取り扱い - 名もないテクノ手



Powered by Web Services by Yahoo! JAPAN

2008-09-30

[][]日本語文章校正ツール

このあいだ書いた「ふりがな(ルビ)を追加」が某所で紹介されて、すっかり気をよくしてしまひ、そーいえばおなじサービスで校正支援もあったよなあ、作ってみようか、などと思ったらもうあった

日本語文章校正ツール

http://www.japaneseproofreader.com/

各種校正オプションも完備しているし、なにより「ブログ校正用リンクボタン」までついていて至れり尽くせり。ですわ。

あんまりよくできているので、すっかりやる気喪失^^ まあ、便利だからいいよね。幸せならそれでいいよね。うう...

2008-08-11

[][][]テキストにふりがな(ルビ)を付加する。

Yahoo!デベロッパーに「ルビ振りWebサービス」APIが上がっていたので、ちょっと遊んでみる。←仕事しろ!

http://developer.yahoo.co.jp/jlp/FuriganaService/V1/furigana.html

やってみたい人はアプリケーションIDを取得してくださいね。

#! /usr/bin/ruby -Ku
=begin
yahoo_ruby.rb
(c)2008 www.seuzo.jp

my_encode	出力文字コード
-j	jis(iso-2022-jp)
-e	EUC_JP
-s	Shift JIS
-w	UTF-8

yahoo_grade	学年を指定します。
1	小学1年生向け。すべての漢字(注2)にふりがなを付けます。
2	小学2年生向け。1年生で習う漢字にはふりがなを付けません。
3	小学3年生向け。1〜2年生で習う漢字にはふりがを付けません。
4	小学4年生向け。1〜3年生で習う漢字にはふりがなを付けません。
5	小学5年生向け。1〜4年生で習う漢字にはふりがなを付けません。
6	小学6年生向け。1〜5年生で習う漢字にはふりがなを付けません。
7	中学生以上向け。小学校で習う漢字にはふりがなを付けません。
8	一般向け。常用漢字にはふりがなを付けません。
=end


require 'cgi'
require 'open-uri'
require 'rexml/document'
require 'nkf'

####パラメータ設定
my_sentence = "私の名前は市川せうぞーです。年齢は43歳&aa<bb>
今日は曇りのち晴れ。今日も真夏日で暑いです。
江戸川乱歩。森鴎外。芥川龍之介、筋肉少女帯、大槻ケンヂ
I Love Green."#元データ
my_sentence = CGI.escape(my_sentence)
my_encode = "-w"#出力文字コード
kanji_delimiter = ""#親文字の区切り文字
furigana_delimiter_begin = ""#ふりがな開始文字
furigana_delimiter_end = ""#ふりがな終了文字
yahoo_grade = "1"

###サーバー側設定
yahoo_service = "http://jlp.yahooapis.jp/FuriganaService/V1/furigana?"#リクエストURL
yahoo_id = "hogehoge"#Yahoo アプリケーションID

my_uri = URI.parse(yahoo_service + "appid=" + yahoo_id + "&grade=" + yahoo_grade + "&sentence=" + my_sentence)
my_xml = my_uri.read
my_doc = REXML::Document.new(my_xml)
my_data = ""

my_doc.elements.each("/ResultSet/Result/WordList/Word/"){|my_Word|
if my_Word.elements["SubWordList"]
	my_Word.each_element("SubWordList/SubWord"){|my_SubWord|
		tmp_Surface = my_SubWord.elements["Surface"].text
		tmp_Furigana = my_SubWord.elements["Furigana"].text
		if tmp_Surface == tmp_Furigana
			my_data += tmp_Surface
		else
			my_data += kanji_delimiter + tmp_Surface
			my_data += furigana_delimiter_begin + tmp_Furigana + furigana_delimiter_end
		end
	}
elsif my_Word.elements["Furigana"]
	my_data += kanji_delimiter + my_Word.elements["Surface"].text
	my_data += furigana_delimiter_begin + my_Word.elements["Furigana"].text + furigana_delimiter_end
else
	my_data += my_Word.elements["Surface"].text
end
}

print NKF.nkf("-W #{my_encode} -m0", my_data)

REXMLの使い方ってこんなんでいいのかな? 間違いなどお気づきのことがありましたらご指摘ください。

Webアプリとして公開するとしたら、いろいろ安全対策とかしないとね。正直めんどい。