読者です 読者をやめる 読者になる 読者になる

キャプション付き図版や表組みの余白をImageMagickでトリミングする

ImageMagick InDesign eBook

 InDesignに配置された図版や表組みを電子書籍用に落とし込む時、いくつかの課題があります。

  • 印刷用データとしてCMYKカラースペースがベースになっている
  • Illustratorの図版ドキュメントに、システムにないフォントが含まれている可能性
  • キャプションを分離させたくない

 電書魂さんの下記記事にもコメントさせていただいたんですが...
「フォントを含む画像」の変換テスト | 電書魂
InDesignはフォントラスタライザを含んでいるので、ドキュメントにフォントが含まれていれば(システムにフォントがなくても)、「表示が質」-「高品質表示」で画面上のフォントが正しく表示されます。
f:id:seuzo:20120609130220p:image
f:id:seuzo:20120609130505p:image

作戦A)スクリーンキャプチャで撮る

 (品質上)可能性の話をすればキリがありませんが、高品質表示でスクリーンキャプチャを取るのは、悪い方法ではないと考えます。少なくとも刷本からスキャニングするよりきれいでしょう*1。CMYKベース→RGBは手間ひまをかけて調整すれば、若干マシにはなるでしょうが原理的にオリジナルカラーに戻るわけではありません。今どき市井の人がデスクトップ上でどれくらいの解像度にしているのかわかりませんが、ウチでは1920×1200pxです。Retinaディスプレイが2048×1536なので、まずまずな大きさを得られるんじゃないでしょうか。
f:id:seuzo:20120609132541j:image
 こんな感じで画面全体に広げて、スクリーンキャプチャを撮りました。*2撮った画像がこちらです。
f:id:seuzo:20120609133230p:image

作戦B)JPEG書出し

 InDesignのJPEG書出しを使えば、選択したオブジェクトだけをJPEG書出しできます。しかも解像度指定もできます。やり方としてはこっちの方が断然ラクですね。
f:id:seuzo:20120609173555p:image

残った余白をどうするか?

 でだ、スクリーンキャプチャを撮る時にどうしても余白が入ってしまいます。これはInDesignドキュメントからJPEG書出しした時も、オブジェクトサイズで書き出されるので余白が残ります。Photoshopで行うとすると、ピクセルが見えるくらい大きくしないとうまくトリミングできません。
 そこでImageMagickを使って、余白をトリミングしましょう。ImageMagickXcodeでもデフォルトインストールではありません。Homebrewなどからインストールするとラクです。
 ImageMagickをインストールしたら、ターミナルからconvertコマンドが実行できるようになります。

$ convert -trim original.png new.png

f:id:seuzo:20120609141319p:image
 余白を取り除くためのトリミングツールは他にもたくさんあるかと思います。今回、ImageMagickを試してみたのは、CLTなので他のスクリプトなどにも組み込みやすいからです。


*1:いうまでもありませんが、解像度が高いことと画質がよいことは必ずしも一致しません

*2:ここで使用しているのはSnapz Pro X http://www.ambrosiasw.com/utilities/snapzprox です。もちろん、これくらい簡単なスクリーンキャプチャを撮るだけなら、command + shift + 4でもかまいません。

exifの解像度を変更してInDesignに貼り込む

InDesign ruby exif

 XMLInDesignに流し込む時、画像の大きさが大きすぎるとページ上に表示すらできずにオーバーフローしてしまいます。これだと変更するだけでもたいへんですね。かといって、すべての画像をきちんと計算してPhotosho上で解像度を整えるのもめんどいです。*1
 通常、PhotoshopやWebブラウザでは、画像の内部的な解像度をちゃんと読み取るのが普通です。しかし、InDesignではexif情報の中に解像度があるとそちらを優先的に使って等倍貼り込みしてくれます。

 ということで、ピクセル数の閾値以上はページ内に収まるようにexifの解像度(X ResolutionとY Resolution)を変更するスクリプトです。変更といっても詐称みたいなもので、内部情報は書き換えていません。おうち使いなので危険度高めです。

#!/usr/bin/ruby
# -*- coding: utf-8 -*-

# change_resolution.rb
# 2012-04-02
# 設定したピクセル数を超えたら、解像度を上げる


require 'mini_exiftool'


##設定
defult_res = 96.0 #デフォルト解像度
x_lim = 1000 #横幅のピクセルリミット
y_lim = 400 #高さのピクセルリミット


#設定の型を浮動小数点に
defult_res = defult_res.to_f
x_lim = x_lim.to_f
y_lim = y_lim.to_f

##ループ
my_file_count_total = ARGV.length
my_file_count_succeed = 0 #成功ファイル数
ARGV.each do |my_file|
	tmp_f = MiniExiftool.new(my_file)
	tmp_f_width = tmp_f['ImageWidth'].to_f
	next if tmp_f_width === nil#幅がなければ次へ
	tmp_f_height = tmp_f['ImageHeight'].to_f
	tmp_f_xres = tmp_f['XResolution'].to_f
	tmp_f_xres = defult_res if tmp_f_xres === 0.0 #X Resolutionが空ならデフォルト解像度にする
	
	tmp_xres = tmp_yres = 0.0
	if tmp_f_width > x_lim then
		tmp_xres = ((tmp_f_width / x_lim) * tmp_f_xres * 1000).round / 1000.0
	end
	if tmp_f_height > y_lim then
		tmp_yres = ((tmp_f_height / y_lim) * tmp_f_xres * 1000).round / 1000.0
	end
	
	#解像度のセット
	set_res = [tmp_xres, tmp_yres].max
	if set_res > tmp_f_xres then
		tmp_f['XResolution'] = set_res
		tmp_f['YResolution'] = set_res
		tmp_f.save
		my_file_count_succeed += 1
	end
end

puts "Total Files: #{my_file_count_total}"
puts "Changed Files: #{my_file_count_succeed}"

 これの閾値を決めるためのバブルチャートだったりします。

*1:Photoshop上で計算するっていう定石ももちろんあります^^

セミナー出演2題

event ANN

最近、めっきり春めいてきましたね! 春だから、というわけじゃありませんが、セミナー講師をふたつさせていただくことになりました。それぞれのスキルアップにお役立ていただければ幸いです。

InDesign正規表現(のWANNA) at 文字の学校

InDesignで正規表現
文字の学校さんには、はじめてお呼ばれいたしました。
InDesign CS3で正規表現が実装されてから、既に5年が経っています。実際にInDesign正規表現を実務でバリバリお使いの方も多いかもしれません。そうした中で「なぜか思い通りにマッチしない時がある」「誤置換で冷や汗をかいた」「いまひとつ自信がない」などという声が聞かれることもしばしばです。
正規表現はメタ文字の種類や定型の解法を暗記することではありません。正規表現にはさまざまな解法があり、考え方やテキストによって使う道具も異なります。本講座では、いくつかの実例を示し注意点(や罠)などを見ていきます。また、第二部では対話形式で課題に取り組み、考え方の一端を感じていただきたいと考えています。
『InDesign者のための正規表現入門』を書かせていただいた時に、大阪東京でやったことの続編です(ネタも若干違います)。ふつーの正規表現ではもう満足できなくなった人向け!

DTP Booster 035「IllustratorJavaScriptで操作する基本と実践」

DTP Booster 035(2012年6月21日、アップルストア銀座で開催)
え? せうぞーがIllustratorスクリプトの講師? と思われる方もいらっしゃるかもしれません。あちこちから石が飛んで来そうな感じがします。
このセミナーでは、既存のスクリプトを改造してみることにチャレンジします。とりあえずやってみる系のショートセミナー(30〜40分)です。スクリプトをエディタで開いただけで心が折れてしまった人向けです。いままでただ使っていたスクリプトに「触れる可能性」を感じていただければ幸いです。*1


(追記:2012-06-29T10:32:52+0900)
DTP Booster 035で使用したスライドをこっそり公開しておきます。
http://www.seuzo.jp/demo/DTP_Booster35_01_Q_A.pdf
http://www.seuzo.jp/demo/DTP_Booster35_02_Correction.pdf

*1:「スクリプトがバリバリ書ける人」が来てもご褒美はありません。逆に怖いです!

たくさんの画像の大きさをバブルチャートでだいたい把握する

ruby exif jQuery

たくさんの画像を扱わなければならない時、だいたいどんな大きさの画像が含まれているか確認しておきたいですよね。バブルチャートでぱっと見られたら便利かな、と思って書いてみました。

バブルチャートは横軸が幅(Image Width)、縦軸が高さ(Image Height)、それぞれ丸の大きさがファイル数を表しています。表はすべてのファイルのピクセル数と解像度が一覧でき、カラムごとにソートできるようになっています。
やってることは至って簡単です。rubyからExifToolexifを得てHTML出力です。バブルチャートはjqPlot、表のカラムソートはTablesorterを使っています。どちらもjQueryプラグインです。適宜ファイルパスなどを変えてお使いください。

#!/usr/bin/ruby
# -*- coding: utf-8 -*-

# pixels_statistical_chart.rb
# 2012-04-01

require 'mini_exiftool'

dot_size = 1 #バブルチャートの最小ドットサイズ

##exifデータの多重配列を得る(数字はString)
data_list = []
ARGV.each do |my_file|
	tmp_f = MiniExiftool.new(my_file)
	tmp_f_width = tmp_f['ImageWidth']
	tmp_f_height = tmp_f['ImageHeight']
	next if tmp_f_width === nil#幅がなければ次へ
	data_list.push([
		tmp_f['FileName'], 
		tmp_f_width.to_s, 
		tmp_f_height.to_s, 
		(tmp_f_width * tmp_f_height).to_s,
		tmp_f['XResolution'].to_s
	])

end


#同じサイズの画像の出現回数をカウント
count_hash = Hash.new(0)
data_list.each{|d_list|
	count_hash[d_list[1] + ',' + d_list[2]] += dot_size
}


#count_hashからチャート用データを生成
chart_data =[]
count_hash.each{|key, value|
	chart_data.push("[#{key},#{value}]")
}
chart_data = chart_data.join(',')


#data_listからtable用データを生成
table_data = ''
data_list.each{|item|
	table_data += "\t\t\t<tr>\n"
	item.each{|child_item|
		table_data += "\t\t\t\t<td>#{child_item}</td>\n"
	}
	table_data += "\t\t\t</tr>\n"
}


#ヒアドキュメントでHTML出力
print <<ENDHTML
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<meta http-equiv="Content-Script-Type" content="text/javascript" />
		<title>pixels_statistical_chart</title>
		<script language="javascript" type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
		<script language="javascript" type="text/javascript" src="dist/jquery.jqplot.min.js"></script>
		<script language="javascript" type="text/javascript" src="dist/plugins/jqplot.bubbleRenderer.min.js"></script>
		<script language="javascript" type="text/javascript" src="__jquery.tablesorter/jquery.tablesorter.js"></script>
		<link rel="stylesheet" type="text/css" href="main.css" />
		<link rel="stylesheet" type="text/css" href="dist/jquery.jqplot.min.css" />
		<link rel="stylesheet" type="text/css" href="__jquery.tablesorter/themes/blue/style.css" />
		
		<script>
jQuery( function() {
    jQuery . jqplot(
        'jqPlot-chart',
        [
            [ #{chart_data} ]
        ],
        {
            title: {
                text: 'ピクセル分布',
                show: true,
                fontFamily: 'sans-serif',
                fontSize: '20px',
                textAlign: 'center',
                textColor: '#704D22',
            },
            seriesDefaults: {
                renderer: jQuery . jqplot . BubbleRenderer,
            }
        }
    );
} );
		</script>

		<script>
$(document).ready(function() 
    { 
        $("#myTable").tablesorter(); 
    } 
); 
		</script>
	</head>
	<body>
		<div id="jqPlot-chart" style="height: 500px; width: 500px;"></div>
		<table id="myTable" class="tablesorter" cellspacing="1">
		<thead> 
			<tr>
				<th>ファイル名</th>
				<th>幅(x pixcel)</th>
				<th>高さ(y pixcel)</th>
				<th>総ピクセル数(pixcel)</th>
				<th>解像度</th>
				</tr>
		</thead> 
		<tbody> 
#{table_data}
		</tbody> 
		</table>

	</body>
</html>
ENDHTML

選択テキストを約物カーニング→文字ツメ→変形(長体・平体)で指定ライン数に追い込む「shorten_line_selection 0.5」

InDesign CS5 JavaScript

何をするスクリプトか?

選択したテキスト部分を

  1. 約物のカーニング
  2. 文字ツメ
  3. 変形(長体・平体)

の順に適用して、指定ライン数以内に収めます。

同梱ファイル2Files

  • README.txt このファイルです。とにかく最初によんでください。
  • shorten_line_selection.jsx スクリプト本体です。

動作環境

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

  • MacOS X10.7.3
  • InDesign CS5_J(7.0.4)

インストール

スクリプト本体(shorten_line_selection.jsx)を
~/Library/Preferences/Adobe InDesign/Version 7.0-J/ja_JP/Scripts/Scripts Panel/
にコピーしてください。エイリアスを入れておくだけでもかまいません。
スクリプトパレットから使用します。

使用方法

  1. 「ウインドウ」メニューから「スクリプティング」ー「スクリプト」を選択し、スクリプトパレットを出します。
  2. 変形をかけたい部分のテキストを選択します。
  3. スクリプトパレットから、スクリプト「shorten_line_selection.jsx」をダブルクリックします。
  4. テキストが変形されたのを確認してください。

設定

スクリプトをエディタまたはESTKで開くと、最初の部分に以下の設定があります。この設定値を変更すれば、簡単にカスタマイズできます。

変数名 初期値 説明
kerning_var -300 約物のカーニング量。限界値ではなく、定数値。:: カーニングしない時は0にする
my_tsume_limit 25 文字ツメの限界値(%):: 適用しない時は0にする
my_scale_limit 70 文字変形の限界値(%):: 適用しない時は0にする
my_line_limit -1 何ライン以内に収めるか。-1を指定すると、常に現状よりも-1ラインとなる。-2は無効。

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

  • カーニング、文字ツメ、変形は縮小方向のみに働きます。例えば、2ライン分を選択していて、3ラインになったりしません。

免責事項

  • 本アプリケーションはInDesignにおける作業効率支援なのであって、処理結果を保証するものではありません。かならず確認をされることをおすすめします。
  • このツールを使用する上でデータの破損などのあらゆる不具合・不利益については一切の責任を負いかねますのでご了解ください。
  • このツールはすべてのMacintoshとMac OS上で動作をするという確認をとっていませんし、事実上出来ません。したがって、動作を保証するものではありません。

履 歴

2010-02-10 ver.0.1 とりあえず
2010-02-15 ver.0.2 my_line_limitに「-1」を指定すると、常に-1ラインにするようにした。
2012-03-14 ver.0.3 最初の
文字ツメを試し、解消しなかった時に変形するようにした。文字変形のルーチンはmilligrammeさんのhttp://www.milligramme.cc/wp/archives/591 を参考にさせていただきました、感謝。文字属性の変更と復帰のために1文字づつのリスト(オブジェクト)にした。こうすることで、現在のツメないし変形に加算されるようにした(そして速度は遅くなった)。文字変形の限界値を超えて復帰するとき、アラートを出すようにした。文字を選択していない時(InsertionPoint)、挿入点の段落を処理対象にした。
2012-03-14 ver.0.4 約物カーニングを最初に試すようにした。ToDo:行ツメに失敗してもカーニング値だけは戻らない。
2012-03-14 ver.0.5 行ツメに失敗してもカーニング値も含めてUndoできるようにした。副作用として、行ツメに成功した場合もUndo一発で復元できるようになった。この、UndoModes.FAST_ENTIRE_SCRIPT使っているので、気持ち程度は速くなったかも。UndoModesの使い方は、@k_kazutoshi さんに教えていただきました。感謝します。https://twitter.com/k_kazutoshi/status/179901033370234880

ソースコード

/*
shorten_line_selection.jsx
選択したテキスト部分を文字ツメ、変形(長体・平体)させて、指定ライン数以内に収めます。

2010-02-10	ver.0.1	とりあえず
2010-02-15	ver.0.2	my_line_limitに「-1」を指定すると、常に-1ラインにするようにした。http://d.hatena.ne.jp/seuzo/20100215/1266213438
2012-03-13  ver.0.3 最初の
文字ツメを試し、解消しなかった時に変形するようにした。文字変形のルーチンはmilligrammeさんのhttp://www.milligramme.cc/wp/archives/591 を参考にさせていただきました、感謝。文字属性の変更と復帰のために1文字づつのリスト(オブジェクト)にした。こうすることで、現在のツメないし変形に加算されるようにした(そして速度は遅くなった)。文字変形の限界値を超えて復帰するとき、アラートを出すようにした。文字を選択していない時(InsertionPoint)、挿入点の段落を処理対象にした。
2012-03-14  ver.0.4 約物カーニングを最初に試すようにした。ToDo:行ツメに失敗してもカーニング値だけは戻らない。
2012-03-14  ver.0.5 行ツメに失敗してもカーニング値も含めてUndoできるようにした。UndoModes.FAST_ENTIRE_SCRIPT使っているので、気持ち程度は速くなったかも。UndoModesの使い方は、@k_kazutoshi さんに教えていただきました。感謝します。https://twitter.com/k_kazutoshi/status/179901033370234880
*/

#target "InDesign"
//////////////////////////////////////////// 設定
var kerning_var = -300 //約物のカーニング量。限界値ではなく、定数値。:: カーニングしない時は0にする
var my_tsume_limit = 25;//文字ツメの限界値(%):: しない時は0にする
var my_scale_limit = 70;//文字変形の限界値(%):: しない時は0にする
var my_line_limit = -1;//何ライン以内に収めるか。-1を指定すると、常に現状よりも-1ラインとなる。-2は無効。



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

////////////////////////////////////////////正規表現検索置換
/*
my_range	検索置換の範囲
my_find	検索オブジェクト ex.) {findWhat:"(わたく?し|私)"}
my_change	置換オブジェクト ex.)  {changeTo:"ぼく"}

my_changeが渡されない時は検索のみ、マッチしたオブジェクトを返す。
my_changeが渡されると置換が実行されて、返値はなし。
*/
function my_RegexFindChange(my_range, my_find, my_change) {
    //検索の初期化
    app.findGrepPreferences = NothingEnum.nothing;
    app.changeGrepPreferences = NothingEnum.nothing;
    //検索オプション
    app.findChangeGrepOptions.includeLockedLayersForFind = false;//ロックされたレイヤーをふくめるかどうか
    app.findChangeGrepOptions.includeLockedStoriesForFind = false;//ロックされたストーリーを含めるかどうか
    app.findChangeGrepOptions.includeHiddenLayers = false;//非表示レイヤーを含めるかどうか
    app.findChangeGrepOptions.includeMasterPages = false;//マスターページを含めるかどうか
    app.findChangeGrepOptions.includeFootnotes = false;//脚注を含めるかどうか
    app.findChangeGrepOptions.kanaSensitive = true;//カナを区別するかどうか
    app.findChangeGrepOptions.widthSensitive = true;//全角半角を区別するかどうか

    app.findGrepPreferences.properties = my_find;//検索の設定
    if (my_change == null) {
        return my_range.findGrep();//検索のみの場合:マッチしたオブジェクトを返す
    } else {
        app.changeGrepPreferences.properties = my_change;//置換の設定
        my_range.changeGrep();//検索と置換の実行
    }
}

////////////////////////////////////////////target_objに含まれる約物を検索して、kerningを実行
function kerning_yakumono(target_obj, kerning_var) {
    ////////////////正規表現文字列の作成と検索(1:起こし括弧類)
    var regex_str = "「『([{‘“〈《【〔";//起こし括弧類
    var hit_obj = [];
    var hit_obj = my_RegexFindChange(target_obj, {findWhat:"[^「『([{‘“〈《【〔 \s\n\r][" + regex_str + "]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var; //カーニング処理(1)
    }
    
    ////////////////正規表現文字列の作成と検索(2:受け括弧類と句読点類)
    regex_str = "」』)]}’”〉》】〕、,。.";//受け括弧類と句読点類
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[" + regex_str + "][^」』)]}’”〉》】〕、,。.\s\n\r]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var; //カーニング処理(2)
    }
    
    ////////////////正規表現文字列の作成と検索(3:中点類)
    regex_str = "・:;";//中点類
    kerning_var2 = kerning_var / 2;
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[" + regex_str + "][^\n\r]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var2; //カーニング処理(3)
    }
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[^\n\r][" + regex_str + "]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var2; //カーニング処理(3)
    }
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[" + regex_str + "][" + regex_str + "]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var; //カーニング処理(3)
    }
}



////////////////////////////////////////////my_objの文字数分の文字属性prop_listを取得してオブジェクトで返す
//ex.) prop_list = ["horizontalScale", "tsume"]
function get_char_prop(my_obj, prop_list) {
    var my_data = new Object();
    for (var i =0; i < prop_list.length; i++){
        my_data[prop_list[i]] = my_obj.characters.everyItem()[prop_list[i]];
    }
    return my_data;
}

////////////////////////////////////////////my_objに文字数分の文字属性prop_objをセット(復帰)する。何も返さない
function set_char_prop(my_obj, prop_obj) {
    for ( var p in prop_obj) {
        if (prop_obj.hasOwnProperty (p)) {
            //alert(p);//
            //alert(prop_obj[p]);
            for (var i = 0; i < my_obj.characters.length; i++) {//セットするときは1文字づつ
                my_obj.characters[i][p] = prop_obj[p][i];
            }
        }
    }
}

////////////////////////////////////////////配列aryの各々の要素にnを加算。要素が数値であることを期待
function add_each_number(ary, n) {
    for (var i = 0; i < ary.length; i++) {
        ary[i] += n;
    }
    return ary
}



////////////////////////////////////////////カーニングと文字ツメと変形
//最初に約物のカーニングを試す。約物のカーニングで収拾できなければ文字ツメ
//次に文字ツメをmy_tsume_limit_numになるまで試してみる。文字ツメで収拾できなければ文字変形へ
//my_target_obj(テキストオブジェクト)を、最大my_scale_limit_num(%)まで変形して、my_line_limit_num(行)以内に収める
var my_func = function set_tsume_scale(args) {
    var my_target_obj = args[0];
    var my_kerning_var = args[1];
    var my_tsume_limit_num = args[2];
    var my_line_limit_num = args[3];
    var my_scale_limit_num =args[4];
    
    if (my_target_obj.lines.length <= my_line_limit_num) {return 1;}//選択行数が指定行数以下ならなにもしない
    var HorV = my_target_obj.parentStory.storyPreferences.storyOrientation == StoryHorizontalOrVertical.HORIZONTAL ? 'horizontalScale':'verticalScale';//my_target_objは縦組みか横組みか。HorVはStrings
    var tmp_tsume_obj = get_char_prop(my_target_obj, ["tsume"]);//文字ツメのオブジェクト
    var tmp_HorV_obj = get_char_prop(my_target_obj, [HorV]);//変形率のオブジェクト。 ディープコピーするのめんどいので、2回つくることにした。
    
    
    //約物カーニング
    if (my_kerning_var !== 0) {
        kerning_yakumono(my_target_obj, my_kerning_var);
        my_target_obj.recompose();//文字列の再描画
        if (my_target_obj.lines.length === my_line_limit_num) {return 1;}//収拾したらreturn
    }
    
    //ツメ
    var tmp_tsume_list = tmp_tsume_obj["tsume"];
    //var tmp_tsume_max =Math.max.apply(null, tmp_tsume_list);//現在のツメ量の最大値
    //myerror(tmp_tsume_list);
    while (my_target_obj.lines.length > my_line_limit_num){
        if (my_tsume_limit_num === 0) {break}//文字ツメはやらない
        if (Math.max.apply(null, tmp_tsume_list) > my_tsume_limit_num) {break}//現在のツメ量の最大値がリミットを超えたら、ツメで調整をあきらめてbreak
        tmp_tsume_list = add_each_number(tmp_tsume_list, 0.01);//各文字のツメを1%づつ加算
        set_char_prop(my_target_obj, tmp_tsume_obj);//ツメを実行
        my_target_obj.recompose();//文字列の再描画
        if (my_target_obj.lines.length === my_line_limit_num) {return 1;}//収拾したらreturn
    }

    //変形しない設定になっている。行ツメは成功していない
    if ((my_scale_limit_num ===0) && (my_target_obj.lines.length > my_line_limit_num)){
        return ("ユーザー設定により、行の変形をおこないませんでした。\n設定:my_scale_limit =0");
    }

    //変形
    var tmp_scale_list = tmp_HorV_obj[HorV];
    while (my_target_obj.lines.length > my_line_limit_num){
        tmp_scale_list = add_each_number(tmp_scale_list, -1);//各文字の変形率を1%づつ減算
        set_char_prop(my_target_obj, tmp_HorV_obj);//各文字の変形を実行
        my_target_obj.recompose();//文字列の再描画
         if (my_target_obj.lines.length === my_line_limit_num) {return 1;}//収拾したらreturn
         if (my_target_obj[HorV] < my_scale_limit_num) {//変形限界を超えてしまったら
             return ("変形限界の設定値" + my_scale_limit_num + "%を超えても" + my_line_limit_num +"行に収まりませんでした。");
         }
     }
}


////////////////////////////////////////////実行処理
function main() {
    if (app.documents.length == 0) {myerror("ドキュメントが開かれていません")}
    var my_document = app.documents[0];
    if (my_document.selection.length !== 1) {myerror("テキストを一カ所だけ選択してください")}
    var my_selection = my_document.selection[0];
    var my_class =my_selection.reflect.name;
    if (my_class === "InsertionPoint") {
        my_selection = my_selection.paragraphs[0];
    } else {
        if (my_selection.paragraphs.length > 1) {myerror("段落をまたいで処理できません")}
        my_class = "Text, TextColumn, Story, Paragraph, Line, Word, Character, TextStyleRange".match(my_class);
        if (my_class === null) {myerror("テキストを選択してください")}
    }
    
    
    //my_line_limitに-1が指定されていた
    if (my_line_limit === -1) {
        my_line_limit = my_selection.lines.length - 1;
        if(my_line_limit < 1) {myerror("設定my_line_limitが不正です")}//1行以上は詰められないので終了
    }
    
    //文字ツメ量は1/100で指定
    if (my_tsume_limit <= 0) {
        my_tsume_limit = 0;
    } else if (my_tsume_limit > 100) {
        my_tsume_limit = 1;
    } else {
        my_tsume_limit = my_tsume_limit / 100;
    }
    
    //処理
    var mess = app.doScript(my_func, ScriptLanguage.JAVASCRIPT, [my_selection, kerning_var, my_tsume_limit, my_line_limit, my_scale_limit], UndoModes.FAST_ENTIRE_SCRIPT, 'MyUndo');
    
    //文字列のメッセージが返ってきたら、失敗したので復元しろって合図
    if (typeof mess === "string") {
        my_document.undo();
        myerror(mess);
    }
}

main();

コードリストや表組みなどで2行以上が折り返している箇所を探したい。

InDesign CS5 JavaScript

 選択している行と同じ段落スタイルを持つ行を検索し、2ラインにまたがっている箇所を表示します。
 例えば、コードリストや表組みなど2行以上の折り返しをチェックしたい箇所を探します。探すだけ。直し方はいろいろなので。
f:id:seuzo:20120312163716p:image

/*
    find_lines_pStyle.jsx
    選択している行と同じ段落スタイルを持つ行を検索し、2ラインにまたがっている箇所を表示します。
    例えば、コードリストや表組みなど2行以上の折り返しをチェックしたい箇所を探します。探すだけ。直し方はいろいろなので。
   
    2012-03-01  とりあえず昔書いたまんま
    2012-03-12  外に出すためにちょっと化粧なおし
*/

#target "InDesign"

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

////////////////////////////////////////////正規表現検索置換
/*
my_range	検索置換の範囲
my_find	検索オブジェクト ex.) {findWhat:"(わたく?し|私)"}
my_change	置換オブジェクト ex.)  {changeTo:"ぼく"}

my_changeが渡されない時は検索のみ、マッチしたオブジェクトを返す。
my_changeが渡されると置換が実行されて、返値はなし。
*/
function my_RegexFindChange(my_range, my_find, my_change) {
    //検索の初期化
    app.findGrepPreferences = NothingEnum.nothing;
    app.changeGrepPreferences = NothingEnum.nothing;
    //検索オプション
    app.findChangeGrepOptions.includeLockedLayersForFind = false;//ロックされたレイヤーをふくめるかどうか
    app.findChangeGrepOptions.includeLockedStoriesForFind = false;//ロックされたストーリーを含めるかどうか
    app.findChangeGrepOptions.includeHiddenLayers = false;//非表示レイヤーを含めるかどうか
    app.findChangeGrepOptions.includeMasterPages = false;//マスターページを含めるかどうか
    app.findChangeGrepOptions.includeFootnotes = false;//脚注を含めるかどうか
    app.findChangeGrepOptions.kanaSensitive = true;//カナを区別するかどうか
    app.findChangeGrepOptions.widthSensitive = true;//全角半角を区別するかどうか

    app.findGrepPreferences.properties = my_find;//検索の設定
    if (my_change == null) {
        return my_range.findGrep();//検索のみの場合:マッチしたオブジェクトを返す
    } else {
        app.changeGrepPreferences.properties = my_change;//置換の設定
        my_range.changeGrep();//検索と置換の実行
    }
}

/////実行
////////////////まずは選択しているもののチェック
if (app.documents.length === 0) {myerror("ドキュメントが開かれていません")}
var my_doc = app.documents[0];
var my_message = "このスクリプトは選択している行と同じ段落スタイルを持つ行を検索し、2ラインにまたがっている箇所を表示します。\n検索したい段落スタイルを持つ行を1行だけ選択してください。";
if (my_doc.selection.length === 0) {myerror(my_message)}
var my_sel = my_doc.selection[0];
var myclass =my_sel.reflect.name;
myclass = "Text, TextColumn, Story, Paragraph, Line, Word, Character, TextStyleRange, InsertionPoint".match(myclass);
if (myclass == null) {myerror(my_message)}
if (my_sel.paragraphs.length !== 1) {myerror(my_message)}

//検索
var my_pStyle = my_sel.appliedParagraphStyle;
var hit_obj = my_RegexFindChange(my_doc, {findWhat:"^.+$", appliedParagraphStyle:my_pStyle});
if (hit_obj.length === 0) {myerror("2行以上になっている行はありませんでした");} 

//表示
var my_zoom = app.activeWindow.zoomPercentage;
for (var ii = 0; ii< hit_obj.length; ii++) {
    if (hit_obj[ii].lines.length > 1) {
        hit_obj[ii].select();
        app.activeWindow.zoomPercentage = my_zoom; //選択位置をフォーカス
        var tmp_pagename = app.layoutWindows[0].activePage.name; 
        var tmp_ans = confirm (tmp_pagename + "ページ:\n以下の部分が行分かれしています。\n" + hit_obj[ii].contents );
        if (tmp_ans === false) {exit();}
    }
}

ReVIEWの外部ファイル取り込み(プリプロセッサ)

ReVIEW

 ReVIEWではドキュメントの中に、外部のテキストファイルの内容やコマンドの結果を取り込む方法が用意されています。ReVIEWドキュメントの中を検索しながら編集しなくても、外部ファイルを差し替えればすべての参照箇所が書き換えられます。

特定の単語を差し込む

 まずは簡単な単語差し込みを試してみましょう。ReVIEWドキュメントと同じ階層に「name.txt」というテキストファイルを用意してください。「name.txt」の中身は自分の名前を書いておきます。
f:id:seuzo:20120309173744p:image
f:id:seuzo:20120309173759p:image
 ReVIEWドキュメントの方は「name.txt」を参照するためにインライン要素@<include>{pathname}を使います。たとえば、実際にはこうなります。

私が担当の@<include>{name.txt}です。

f:id:seuzo:20120309174456p:image
 このReVIEWドキュメントからHTMLを生成すると、「name.txt」の内容を差し込みしました。
f:id:seuzo:20120309174913p:image
 このname.txtにはReVIEW記法が使えます。テキストの差し込みが行われた後に、ReVIEW記法が評価されているようです。
f:id:seuzo:20120309175307p:image
f:id:seuzo:20120309175310p:image
 ちなみに、@<include>{pathname}はパスを理解するので、「name.txt」がimagesディレクトリの中にあれば、@<include>{images/name.txt}と書けるでしょう。

段落を含んだ文章を差し込む

 @<include>{pathname}はあくまでインライン要素として文中に差し込むためのコマンドです。別管理されたコードリストなどの外部ファイルの内容など、複数行の段落にわたるブロックとして取り込みたいときには、ちょっと手順を踏む必要があります。

(1)外部ファイルを用意する
 ここでは「script.rb」というテキストファイルを用意しました。
f:id:seuzo:20120309232059p:image
f:id:seuzo:20120309232008p:image

(2)オリジナルReVIEWドキュメントにファイル指定する
 オリジナルReVIEWドキュメント「ch02_org.re」の中の挿入位置に下記マーキングをします。

#@mapfile(script.rb)
#@end

f:id:seuzo:20120309232544p:image

(3)review-preprocコマンドで外部ファイルをマージする
 ターミナルから下記review-preprocコマンドをタイプします。

$ review-preproc ch02_org.re > ch02.re

 プリプロセッサで生成した「ch02.re」を見てみましょう。
f:id:seuzo:20120309233133p:image
 「script.rb」の内容が「ch02.re」の中に挿入されているのがわかります。あとで「script.rb」の中を修正したとしても、プリプロセッサで再マージすれば常に新しいReVIEWドキュメントが生成されます。「#@mapfile(script.rb)」と「#@end」の行は「ch02.re」内に残っていますが、「review-compile」コマンドで変換時、EPUBXMLなどの最終生成物には残りません。
 一連の手順を図にするとこんな感じになります。
f:id:seuzo:20120309233811p:image
 「#@mapfile(pathname)」で展開されるテキスト中にあるタブ文字は、すべてスペースに変換されますから注意してください。もしタブ文字のままであったら、表組みのTSVなども差し込みできるのになあ... と思います。

外部ファイルの一部だけを利用する

 外部ファイルの一定の範囲だけを選択的に差し込むためには

#@maprange(filepath, rangename)
#@end

を使うらしいのですが、ちょっとよくわかっていません。MLでそれらしい議論もあるのですが...
外部ファイルを読み込む記法 - qwik.jp/review
 詳細が分かり次第、追記したいと思います。
(追記:2012-06-06T16:17:57+0900)http://qwik.jp/review/81.html で高橋征義さんによってmaprange()の使い方が解説されていました。以下テストしてみました。

(1)外部ファイルの取り込みたい部分を「#@range_begin(rangename)」から「#@range_end(rangename)」で囲います。*1
 ここでは「script04.rb」というファイル内で「config」というラベル名で範囲を指定しました。
f:id:seuzo:20120606185136p:image

(2)オリジナルReVIEWドキュメントにファイルとラベルを指定する
f:id:seuzo:20120606185625p:image

(3)review-preprocコマンドで外部ファイルをマージする

$ review-preproc ch04_org.re > ch04.re

 「ch04.re」は指定範囲が取り込まれているのが分かります。
f:id:seuzo:20120606185853p:image
f:id:seuzo:20120606190422p:image

コマンドの実行結果を取り込む

 「#@mapoutput(コマンド) 〜 #@end」を使えば、コマンドの実行結果を取り込むことができます。
 たとえば、今年の年号を表記する時などはこのように書くことができます。

#@mapoutput(date +"%Y")
#@end

f:id:seuzo:20120311220524p:image
 review-preprocコマンド後には展開されてこうなります。
f:id:seuzo:20120311220810p:image
 review-compileでHTMLを生成するとこうなります。
f:id:seuzo:20120311221012p:image

 コマンドで展開できるのは、既存のアプリケーションだけではありません。テキストを返すものならば、自作のスクリプトでもよいのです。たとえば、昨日こちらのアップした「特定のTweet引用時の正書法」をつかってみましょう。

#@mapoutput(ruby19 cite_a_tweet.rb "https://twitter.com/#!/never4get_jp/status/178718486745452544")
#@end

f:id:seuzo:20120311222418p:image
 review-preprocコマンド後には展開されてこうなります。
f:id:seuzo:20120311222850p:image
 review-compileでHTMLを生成するとこうなります。
f:id:seuzo:20120311223217p:image

*1:この目印のためのマーキングは「^#@range_begin(.+?)」でなければなりません。行頭から「#」始まりである必要があります。#がコメント扱いになる言語に最適化されています