以下では MS-Word の箇条書きについて記します。
サンプルのスクリプトは、いずれもrubyスクリプトです。 list_wrd.zipに含まれています。
どのサンプルも、MS-Wordを自動操縦するためのruby用ライブラリ wrdap.rb を用いる前提になっています。
拙作wrdap.rb(ワード自動操縦のためのruby用ライブラリ)は、pandocというコマンドとの連携を意式しています。pandocは、markdownの原稿をワード文書に変換する機能を持つソフトウェアです。pandocで行えない細かな調整を、実際にワードを起動して行う目的で、wrdap.rbを作りました。
ところで、pandocで箇条書きを行った場合、「@」のような、数字を丸で囲んだ番号を付けることができません。
そこで、markdownの原稿の段階ではローマ数字の箇条書きにしておいて、それをワードの自動操縦で丸付きの数字に変更することを考えました。
当初 予想していたより複雑で、ワードの箇条書きに関するルールをなかなか把握できませんでしたが、そこそこ分かってきたのでメモとして残しておきたいと思います。
なお、以下に掲げるrubyスクリプトは、ruby ver 2.1.5, Word2010 で動作確認しました。
まず、pandocで扱える箇条書きの記法について記します。markdownの原稿を書く時のルールです。
行頭に記号(*, +, -のいずれか)と続けて半角スペース1つ以上を置くと、それが記号付きの箇条書きになります。
箇条書きを入れ子にすることもできます。その場合、行頭に半角スペース4個、または1つのタブを置きます。
更に深い階層の箇条書きにするには、スペース8個またはタブ2個を置くことになります。
pandocは、1. のように、半角の数字に続けてピリオドとスペースを置くと、順序付きの箇条書きとみなして文書を生成します。
数字の他に、大文字または小文字のアルファベット、あるいは、大文字または小文字のローマ数字をリストマーカとして使うことができます。
リストマーカは括弧で囲むか、その後ろに閉じ括弧またはピリオドを置きます。そして、その後に半角スペースを続けます。
なお、リストマーカが大文字とピリオドで構成されている場合は、2つ以上のスペースを置く必要があるので注意して下さい。
箇条書きを入れ子にする場合のルールは、先述の記号付きの箇条書きと同じです。
以下に markdownの原稿の例を掲げておきます。
−−−−−−−− ここから
* 日本
- 東京
+ 足立区
+ 荒川区
- 神奈川県
+ 横浜市
+ 川崎市
* アメリカ合衆国
- テキサス州
+ ヒューストン
+ ダラス
- カリフォルニア州
+ ロサンゼルス
+ サクラメント
1. 果物
(1) リンゴ
(2) バナナ
2. さかな
I. カレイ
II. ニシン
III. ヒラメ
3. 野菜
(a) トマト
(b) レタス
−−−−−−−− ここまで
上の「I.」とか「II.」の後に、半角スペース2個を置いてあります。
pandocで生成したワード文書の箇条書きにおいて、ローマ数字を丸付きの数字に変換するスrubyクリプトを下に掲げます。
これにはいろいろな解説を加える必要がありますが、まずはどんなスクリプトになるのかを示してみます。
list01.md というmarkdownの原稿をpandocによってワード文書に変換し、次に、ワードを起動して箇条書きの変更を行います。
なお、ワード関連の解説では、箇条書きのことをリスト(list)と表現することが多いようです。
また、箇条書きの先頭にある「●」などの記号を「行頭文字」、「1.」とか「(1)」などを「行番号」と表現するようです。
−−−−−−−− list01.rb ここから
# 箇条書きの大文字ローマ数字を丸囲みの数字に変更 (encoding: Windows-31J)
require "wrdap"
# pandocで箇条書きの文書を作成
docx_file = "list01.docx"
mkd_str = File.read("list01.md")
docx_str = Wrd::pandoc_docx(mkd_str)
File.open(docx_file, "wb") {|ff| ff.write docx_str}
# ワードで箇条書きのローマ数字を丸囲みの数字に変更
doc_file = docx_file.sub(/x$/i, "")
Wrdap.new(docx_file) do |doc|
for pn in 1..doc.Paragraphs.Count
para = doc.Paragraphs(pn)
if para.Range.ListParagraphs.Count < 1 # 箇条書きが含まれていない
next
end
fmt = para.Range.ListFormat
tpl = fmt.ListTemplate
for li in 1..tpl.ListLevels.Count
lvl = tpl.ListLevels(li)
nf = lvl.NumberFormat # 行番号の書式
nf2 = (nf =~ /(\%\d+)/) ? $1 : nf # 新しい行番号の書式
ns = lvl.NumberStyle # 行番号の種類
if ns == WdListNumberStyleUppercaseRoman # 大文字ローマ数字形式
tpl.ListLevels(li).NumberStyle = WdListNumberStyleNumberInCircle
tpl.ListLevels(li).NumberFormat = nf2
end
end
end
doc.save doc_file
end
File.unlink(docx_file) # pandocで作ったワード文書を削除
−−−−−−−− list01.rb ここまで
詳しいことは後述しますが、大雑把な説明を加えておきます。
一定のRangeに含まれる箇条書き段落の集まりを取り出すのに用います。
Range.ListParagraphs.Count を見れば、そのRangeに含まれている箇条書き段落の個数を知ることができます。
上の list01.rb では、ワード文書の段落(paragraph)を1つづつ取り出して、変数 para に代入する形になっています。para.Range.ListParagraphs.Count が0であれば、その段落には箇条書きが含まれていないことになります。
該当の段落の箇条書きに関する様々な情報を取り出したいとき、まずは ListFormat にアクセスするのが常套手段のようです。
「fmt = para.Range.ListFormat」のように用います。
例えば、fmt.ListString は、箇条書きの行番号(1. とか a. など)を取得します。fmt.ListValue は、その段落が階層中で何番目の行であるかを取得します。
適用されている箇条書きのテンプレートを取得します。
「tpl = fmt.ListTemplate」のようにして取り出します。ListFormatの下にぶら下がっています。
ワードには、予め用意されている既定の箇条書きテンプレートが21種類あるようですが、pandocは、それらとは別に自前のテンプレートを設けて用いているようです。
テンプレートに直接ぶら下がっているもので、最も必要になるのが次に掲げる ListLevels だと思います。
箇条書きの各レベルに関する情報を取得します。ここでいうレベルをきちんと説明するためにはアウトラインに触れなければなりませんが、とりあえずは箇条書きの階層に該当するものと思っていいと思います。
「lvl = tpl.ListLevels(1)」のようにして取得します。括弧内の番号は、1〜9 のどれかです。1が最も浅い階層、9が最も深い階層を示します。
list01.rbでは、各レベルを1つづつ取り出して扱っています。
この ListLevel の下に、NumberStyle(行番号の種類)とか NumberFormat(行番号の書式)などがあります。今回アクセスしたかったのは、こうしたプロパティです。
次項では、とりあえず pandoc との関連性を離れて、そもそものワードでの箇条書きの扱い方について記します。
箇条書きの行頭文字や行番号を定めているのは、ListLevelの下にある各種のプロパティです。
それらの値を見れば、どんな箇条書きかを把握できます。また、それらの値を変更すれば、箇条書きの様子を別の形に切り替えることができます。
ここでは、主なプロパティについて説明します。
なお、ListLevelは、ListFormatの下のListTemplateにぶら下がっています。また、1つのテンプレートの中にListLevelが複数あることも少なくありません。たどり着くのに手間がかかるので、スクリプトを書く時は念頭に置いておいて下さい。
ListLevelの下に NumberStyle というプロパティがあります。
NumberStyleを変更することによって、箇条書きの行番号をアラビア数字にしたり、ローマ数字にしたり、アルファベットにしたり、漢数字にしたりできます。
といっても、NumberStyleには文字列を設定するわけではありません。整数値を設定します。
例えば、0がアラビア数字、1はローマ数字(大文字)、10だと漢数字です。
下に NumberStyle で設定できる値の一覧を掲げます。59種類あります。日本語ワードでは使えないものもあるようですが、参考まで掲げておきます。
定数名 | 値 | 意味 |
---|---|---|
WdListNumberStyleArabic | 0 | アラビア数字形式 |
WdListNumberStyleUppercaseRoman | 1 | ローマ数字形式(大文字) |
WdListNumberStyleLowercaseRoman | 2 | ローマ数字形式(小文字) |
WdListNumberStyleUppercaseLetter | 3 | アルファベット形式(大文字) |
WdListNumberStyleLowercaseLetter | 4 | アルファベット形式(小文字) |
WdListNumberStyleOrdinal | 5 | 序数形式 |
WdListNumberStyleCardinalText | 6 | 標準文字形式 |
WdListNumberStyleOrdinalText | 7 | 序数文字列形式 |
WdListNumberStyleKanji | 10 | 漢数字形式 |
WdListNumberStyleKanjiDigit | 11 | 漢数字形式(算用数字) |
WdListNumberStyleAiueoHalfWidth | 12 | アイウエオ形式(半角) |
WdListNumberStyleIrohaHalfWidth | 13 | イロハ形式(半角) |
WdListNumberStyleArabicFullWidth | 14 | アラビア数字形式(全角) |
WdListNumberStyleKanjiTraditional | 16 | 漢数字形式(大字) |
WdListNumberStyleKanjiTraditional2 | 17 | 漢数字形式(大字)(2) |
WdListNumberStyleNumberInCircle | 18 | 丸付き数字形式 |
WdListNumberStyleAiueo | 20 | アイウエオ形式 |
WdListNumberStyleIroha | 21 | イロハ形式 |
WdListNumberStyleArabicLZ | 22 | アラビア数字形式(LZ) |
WdListNumberStyleBullet | 23 | 行頭文字形式 |
WdListNumberStyleGanada | 24 | Ganada形式 |
WdListNumberStyleChosung | 25 | Chosung形式 |
WdListNumberStyleGBNum1 | 26 | GB数字形式(1) |
WdListNumberStyleGBNum2 | 27 | GB数字形式(2) |
WdListNumberStyleGBNum3 | 28 | GB数字形式(3) |
WdListNumberStyleGBNum4 | 29 | GB数字形式(4) |
WdListNumberStyleZodiac1 | 30 | 干支形式(1) |
WdListNumberStyleZodiac2 | 31 | 干支形式(2) |
WdListNumberStyleZodiac3 | 32 | 干支形式(3) |
WdListNumberStyleTradChinNum1 | 33 | 繁体字中国語の数字形式(1) |
WdListNumberStyleTradChinNum2 | 34 | 繁体字中国語の数字形式(2) |
WdListNumberStyleTradChinNum3 | 35 | 繁体字中国語の数字形式(3) |
WdListNumberStyleTradChinNum4 | 36 | 繁体字中国語の数字形式(4) |
WdListNumberStyleSimpChinNum1 | 37 | 簡体字中国語の数字形式(1) |
WdListNumberStyleSimpChinNum2 | 38 | 簡体字中国語の数字形式(2) |
WdListNumberStyleSimpChinNum3 | 39 | 簡体字中国語の数字形式(3) |
WdListNumberStyleSimpChinNum4 | 40 | 簡体字中国語の数字形式(4) |
WdListNumberStyleHanjaRead | 41 | Hanja綴り字形式 |
WdListNumberStyleHanjaReadDigit | 42 | Hanja綴り字形式(算用数字) |
WdListNumberStyleHangul | 43 | ハングル形式 |
WdListNumberStyleHanja | 44 | Hanja形式 |
WdListNumberStyleHebrew1 | 45 | ヘブライ語形式(1) |
WdListNumberStyleArabic1 | 46 | アラビア数字形式(1) |
WdListNumberStyleHebrew2 | 47 | ヘブライ語形式(2) |
WdListNumberStyleArabic2 | 48 | アラビア数字形式(2) |
WdListNumberStyleHindiLetter1 | 49 | ヒンディー語文字形式(1) |
WdListNumberStyleHindiLetter2 | 50 | ヒンディー語文字形式(2) |
WdListNumberStyleHindiArabic | 51 | ヒンディー語形式(アラビア数字) |
WdListNumberStyleHindiCardinalText | 52 | ヒンディー語標準文字形式 |
WdListNumberStyleThaiLetter | 53 | タイ語文字形式 |
WdListNumberStyleThaiArabic | 54 | タイ語形式(アラビア数字) |
WdListNumberStyleThaiCardinalText | 55 | タイ語標準文字形式 |
WdListNumberStyleVietCardinalText | 56 | ベトナム語標準文字形式 |
WdListNumberStyleLowercaseRussian | 58 | ロシア語形式(小文字) |
WdListNumberStyleUppercaseRussian | 59 | ロシア語形式(大文字) |
WdListNumberStylePictureBullet | 249 | 行頭絵文字形式 |
WdListNumberStyleLegal | 253 | 法律形式 |
WdListNumberStyleLegalLZ | 254 | 法律形式(LZ) |
WdListNumberStyleNone | 255 | 適用形式なし |
「行頭文字形式」と「行頭絵文字形式」は、行番号ではなく「●」とか「■」などの記号がくる形式のことです。
行番号の書式を設定する時に NumberFormat を用います。
NumberStyleにアラビア数字形式を設定したとしても、「1.」のようにピリオドを付ける書式、「(2)」のように括弧で囲む書式などいろいろあります。どのような書式にするかをNumberFormatで定めます。
「lvl.NumberFormat = “%1.”」とすれば「1.」とか「2.」になります。「”(%1)”」にすれば、「(1)」とか「(2)」などになります。%1 のところに該当の番号が割り当てられます。
%1 だけでなく %2 や %3 を使うケースもあります。箇条書きレベルが最も浅い番号を割り当てるのが %1 で、次に浅いのが %2、最も深いレヘルの番号は %9 ということになります。
テンプレートの箇条書きのレベルが1つだけなら %1 しか使いませんが、レベルが2つある場合であって、番号を「1.1」とか「1.2」あるいは「2.3」などのように付けたい時は、レベル2の書式を「”%1.%2”」とします。
目次を箇条書きで作成する場合、最も大きい項目が「第1章」のような「章」、次が「節」、その下に「項」がくるケースでいうと、次のように設定します。
tpl.ListLevels(1).NumberFormat = "第%1章"
tpl.ListLevels(2).NumberFormat = "第%2節"
tpl.ListLevels(3).NumberFormat = "第%3項"
NumberFormat は、値を設定するだけでなく、設定されている値を取り出す時も使うことができます。つまり、読み書き両用です。
ただし、「行番号形式」ではなく、「行頭文字形式」と「行頭絵文字形式」の場合、rubyでNumberFormatの値を取り出すと “?” になってしまう場合があります。ワードでいうところの「記号と特殊文字」の特別の文字が割り当てられている時にそうなります。ワードに予め用意されている既定のテンプレート(行頭文字形式と行頭絵文字形式のもの)は、いずれも “?” になってしまいます。
行番号または行頭文字の後に挿入する文字(スペースまたはタブ)を設定します。
設定できる値は次の3つです。整数値を与えます。
定数名 | 値 | 意味 |
---|---|---|
WdTrailingTab | 0 | タブを挿入 |
WdTrailingSpace | 1 | スペースを挿入 |
WdTrailingNone | 2 | 文字を挿入しない |
既定のテンプレートを確認すると、多くが「タブを挿入」になっているようです。pandocで生成されたワード文書も同じく「タブを挿入」に設定されています。
ListLevelには、他にも様々なプロパティがあります。それぞれについて詳述するだけの材料は持っていませんが、要点だけ列記しておきます。
ワードには、3×7=21種類の箇条書きテンプレートが用意されています。ここでは、それらの情報を取得することを試みます。
箇条書きのテンプレートを取り出すためには、まずギャラリーというのを扱う必要があります。ギャラリーは、テンプレートを分類するための大枠のようなものです。
VBA風に書くと、Application.ListGalleries(1) のようにしてギャラリーの1つを取り出します。
ギャラリーには次の3種類があります。
定数名 | 値 | 意味 |
---|---|---|
WdBulletGallery | 1 | 行頭文字・行頭絵文字形式 |
WdNumberGallery | 2 | 行番号形式 |
WdOutlineNumberGallery | 3 | アウトライン形式 |
上の3種類のギャラリーそれぞれの下に、7つのテンプレートがあります。なので、テンプレートの総数は 21 となります。
アウトライン形式というのは、階層構造を持つ箇条書きに適用する形式です。この分類に属するテンプレートは、9つの箇条書きレベルを持ちます。
行番号形式および行頭文字・行頭絵文字形式のテンプレートは、1つのレベルしか持ちません。
1つのギャラリーに焦点を当てて、その下にある1番目のテンプレートを取り出す時は、VBA風に書くと次のようにします。
Application.ListGalleries(1).ListTemplates(1)
先に示した list01.rb では、文書の各段落(paragraph)に着目しながらテンプレートを取り出しました。その時は、まず ListFormat を取得した上で、その下にある ListTemplate にアクセスしたわけですが、ギャラリー経由の場合は、ギャラリーの直下にテンプレートがあります。
以下に、21種類のテンプレート各々について、そのListLevelの行番号スタイル(NumberStyle)や行番号の書式(NumberFormat)などの情報を標準出力に出力する rubyスクリプトを掲げます。
NumberStyleを数値で示されても訳が分からないので、「アラビア数字形式」のように文字で出力するようにしてあります。
そうするために rubyのハッシュを用いていますが、ハッシュの定義部分を含めるとスクリプトが長くなるので、下では該当の箇所を省略しています。
ハッシュは、notes[‘NumberStyle’][0]が「アラビア数字形式」、また、notes[‘TrailingCharacter’][0]が「タブ」などと定義してあります。
−−−−−−−− list02.rb ここから
# encoding: Windows-31J
require "wrdap"
(中略)
wrd = Wrdap.new
for g_type in 1..3
case g_type
when WdBulletGallery
puts "□ 行頭文字タイプ"
when WdNumberGallery
puts "□ 行番号タイプ"
when WdOutlineNumberGallery
puts "□ アウトラインタイプ"
end
for tpl_num in 1..wrd.ListGalleries(g_type).ListTemplates.Count
tpl = wrd.ListGalleries(g_type).ListTemplates(tpl_num)
printf("△ Template#%d\n", tpl_num)
lvl_count = tpl.ListLevels.Count # 含まれているListLevelの個数
for li in 1..lvl_count
lvl = tpl.ListLevels(li)
if lvl_count > 1
printf("◇ レベル#%d\n", lvl.Index) # ListLevelの番号を出力
end
props.each do |prop| # ListLevelの各プロパティにアクセス
v = eval("lvl.#{prop}") # プロパティの値
if notes[prop] # 注釈が定義されている場合
printf("%s\t%s\n", prop, notes[prop][v])
if prop == 'NumberStyle' and v == WdListNumberStyleNone
printf("(以下、省略)\n") # 適用形式なしなので省略
break
end
else
printf("%s\t", prop)
p v
end
end
printf("\n")
end
end
end
wrd.quit
−−−−−−−− list02.rb ここまで
上のスクリプトを実行すると、21種類それぞれのテンプレートに関する情報が出力されます。
ただ、先にも書いたように「行頭文字形式」の場合は NumberFormat の値が “?” になってしまいます。
それを補う意味で、どんな行頭文字が割り当てられているのかを記しておきます。いずれも Font.Name は “Wingdings” です。
Template番号 | 記号 | 説明 | unicode |
---|---|---|---|
1 | ● | 黒丸 | F06C |
2 | ■ | 黒の正方形 | F06E |
3 | ◆ | 45度傾いた黒の正方形 | F075 |
4 | 行頭絵文字 | 3色の色がついたマーク | ???? |
5 | チェックマーク | F0FC | |
6 | 右向きの三角 | F0D8 | |
7 | □ | 白い四角 | F0B2 |
unicodeの例えば「F06C」は、rubyでは “\uF06C” と書くことができます。この書き方を用いる例は後述します。
今度は、既定のテンプレートを適用して箇条書きの文書を作ります。pandocは使いません。
英国の科学者4人の氏名を箇条書きで列記します。箇条書きの前後に、通常の段落(箇条書きでない行)を置きます。
ワードの新規文書を開いて、そこに1行づつ書き込んでいく形で文書を作ります。
ワードの1つの段落(paragraph)に箇条書きテンプレートを適用する時は、ListFormat.ApplyListTemplate() を用います。
1つの段落が変数 para に代入されている場合でいうと、次のようにします。
para.Range.ListFormat.ApplyListTemplate('ListTemplate'=>tpl)
上の変数 tpl には、予めテンプレートオブジェクトを代入しておきます。
この ApplyListTemplate() は、箇条書きが始まる時に実行します。箇条書きの2行目、3行目の時は実行しません。1行目に適用したのが引き継がれます。
そして、箇条書きを解除する時は、段落のスタイルを「標準」に設定します。段落が変数 para に代入されている場合、次のようにします。
para.Range.Style = WdStyleNormal
こうすると、段落が箇条書きでない通常の段落になります。
以下に list03.rb を掲げます。2番のギャラリーの1番のテンプレートを用います。「1. 2. 3.」の行番号形式です。
−−−−−−−− list03.rb ここから
# encoding: Windows-31J
require "wrdap"
list_ary = %w(アイザック・ニュートン チャールズ・ダーウィン) +
%w(アレクサンダー・フレミング アラン・チューリング)
filename = "list03.doc" # これから作るワード文書の名前
File.unlink(filename) if test(?e, filename)
wrd = Wrdap.new # ワードの起動
tpl = wrd.ListGalleries(2).ListTemplates(1) # 既定のテンプレート
wrd.opens(filename) do |doc| # ワード文書のオープン
slc = doc.Application.Selection
pn = 1 # 段落番号
slc.TypeText "英国の代表的科学者"
slc.TypeParagraph() # エンターキーを押すのに相当
pn += 1 # 段落番号を1つ増やす
for ln in 0...list_ary.size # 箇条書きの処理
if ln == 0 # 箇条書きの1行目なのでテンプレートを適用
doc.Paragraphs(pn).Range.ListFormat.ApplyListTemplate(
'ListTemplate'=>tpl)
end
slc.TypeText list_ary[ln]
slc.TypeParagraph() # エンターキーを押すのに相当
pn += 1 # 段落番号を1つ増やす
end
doc.Paragraphs(pn).Range.Style = WdStyleNormal # 標準スタイルに戻す
slc.TypeText "(以下、省略)"
slc.TypeParagraph() # エンターキーを押すのに相当
doc.save # ワード文書の保存
end
wrd.quit
−−−−−−−− list03.rb ここまで
既定のテンプレートを適用する例をもう一つ掲げておきます。
テキストファイルを行単位に分解して、それをワード文書に1行づつ入力する形を採ります。
箇条書きの行頭文字として「● ■ ◆」の3つを扱います。行番号としては「1. @」の2種類の番号を扱います。つまり、既定のテンプレートのうち5種類を用います。
テキストファイルの中に、行頭が「●」とか「1.」などで始まる行があると、それを箇条書きに変換します。
箇条書きにすべきかどうかの判断は、正規表現パターンによって行います。
以下にサンプルスクリプトの list04.rb を掲げます。テキストファイル(list04.txt)を取り込んで行ごとに分解する部分は省略します。
ハッシュの gal_tpl を定義する箇所で、「’■’=>[1,2]」のような記述が出てきますが、これは、行頭が「■」の場合、1番目のギャラリーの2番目のテンプレートを適用する、という意味を込めたものです。
−−−−−−−− list04.rb ここから
# encoding: Windows-31J
require "wrdap"
(中略)
mark_ptn = ['●', '■', '◆', '\d+\.', '[@-S]'] # 箇条書きの行頭パターン
gal_tpl = { # 箇条書きのテンプレート取り出し用ハッシュ
'●'=>[1,1], '■'=>[1,2], # Gallery&templateの番号の組を定義
'◆'=>[1,3], '1.'=>[2,1], '@'=>[2,2]}
premark = nil # 前の行の行頭文字をこれに記録
wrd = Wrdap.new # Wordの起動
wrd.opens(filename) do |doc|
slc = doc.Application.Selection
pn = 1 # 段落番号
lines.each do |line| # 読み込んだテキストファイルを1行づつ処理
mark = new_str = nil
mark_ptn.each do |ptn|
if line =~ /^(#{ptn})\s*(.+)$/ # 箇条書きパターーンにマッチ
mark = $1 # 行頭文字または行番号
new_str = $2
break
end
end
if mark == nil # 箇条書きではない
if premark != nil # 前の行が箇条書きだった
doc.Paragraphs(pn).Range.Style = WdStyleNormal # 標準スタイルに
end
slc.TypeText line
elsif mark_ptn[0..2].include?(mark) # 行頭文字の場合(行番号ではない)
if premark != mark # 前の行とマークが異なる
gn, tn = gal_tpl[mark]
tpl = wrd.ListGalleries(gn).ListTemplates(tn)
doc.Paragraphs(pn).Range.ListFormat.ApplyListTemplate(
'ListTemplate'=>tpl, 'ContinuePreviousList'=>false)
end
slc.TypeText new_str
else # 行番号の場合
if gal_tpl[mark] # 「1.」か「@」の場合
gn, tn = gal_tpl[mark]
tpl = wrd.ListGalleries(gn).ListTemplates(tn)
doc.Paragraphs(pn).Range.ListFormat.ApplyListTemplate(
'ListTemplate'=>tpl, 'ContinuePreviousList'=>false)
end
slc.TypeText new_str
end
premark = mark # 今回の段落の行頭マークを記録
slc.TypeParagraph() # エンターキーを押すのに相当
pn += 1 # 段落番号を1つ増やす
end
doc.save # ワード文書の保存
end
wrd.quit
−−−−−−−− list04.rb ここまで
今回、ApplyListTemplate() の引数に「’ContinuePreviousList’=>false」というのを加えています。これは、「この先の箇条書きは、これまでの箇条書きの連続番号とは違って1番から付け直します」という宣言です。箇条書きの番号の継続性を否定するものです。
あまりないとは思いますが、「1.」の形式から「@」の形式にいきなり切り替わった場合、この ContinuePreviousList の指定がないと、確かに形式は切り替わるのですが、番号が継続されてしまいます。つまり、1.〜3. の次が、@ではなくCになってしまいます。
ちなみに、「1.」の形式の箇条書きと、「@」の形式の箇条書きとの間に、箇条書きでない標準スタイルの段落が入るようであれば、ContinuePreviousListを指定しなくても番号が継続されることはありません。
くどくなるのでここには掲げませんが、 list04b.rb というのを圧縮ファイルに同梱してあります。
これは、list04.rbと同じワード文書を作りますが、テキストファイル list04.txt を最初に全部取り込んでしまってから、各段落(paragraph)を調整するやり方を採っています。テキストを行単位に分割して、その1行づつをワード文書に入力するわけではありません。
この場合、最初は全部の段落が標準スタイルになっているわけですが、行頭のマークを手がかりにして、箇条書きにすべきものは箇条書きに変更します。
箇条書きに設定し直す時に、単に ApplyListTemplate() を適用するだけでは駄目なので、段落のスタイルを箇条書きスタイルに変更します。次のようにします。
para.Range.Style = WdStyleListBullet # 行頭文字形式の箇条書き
para.Range.Style = WdStyleListNumber # 行番号形式の箇条書き
上の2つのうちのどちらかを記述しますが、この記述は、ApplyListTemplate() よりも前に置く必要があるので注意して下さい。
既定のテンプレートではなく、自前のテンプレートを設けて適用したいことがあります。
例えば、既定のテンプレートにはアラビア数字を括弧で囲む「(1)」に該当するものが見あたりません。この形式を適用したい時は、テンプレートを新たに設ける必要があります。
list04.rbと同じようなやり方で、テキストを取り込んでワード文書を生成してみます。
行頭が「・」で始まる行を箇条書きにしますが、行頭文字形式ではなく、「(1)」の行番号形式にします。
箇条書きは、とりあえず簡単な単一レベル(階層が1つだけ)のものにします。
ところで、自前のテンプレートを設ける時は、ギャラリーの下に追加するのでなく、1つのワード文書(ActiveDocument)に帰属する形で設けます。
ギャラリーの下にある既定のテンプレート群のところに、自前のテンプレートを追加・挿入することはできないようです。
VBA風に書くと、次のようにすればテンプレートを新たに設けることができます。
ActiveDocument.ListTemplates.Add
上のようにして設けたテンプレートの中身がどうなっているかというと、次のようになっています。
・NumberStyle: アラビア数字形式
・NumberFormat: “%1.”
・TrailingCharacter: タブ
・NumberPosition: 0.0
・Alignment: 左揃え
・StartAt: 1
・TextPosition: 21.0
これを「(1)」の形式に修正するには、NumberFormat を “(%1)” にすればいいことになります。
なお、新たに設けたテンプレートの ListLevels の要素個数は1です。つまり、1つの箇条書きレベルしか持たないテンプレートです。複数のレベルを持つテンプレートについては後述します。
以下に list05.rb を掲げます。スクリプトの最後の方にあるテキスト(__END__の後ろ)を取り込んでワード文書を生成します。
行頭に「・」があるものは、「(1)」の形式の箇条書きにします。
−−−−−−−− list05.rb ここから
# encoding: Windows-31J
require "wrdap"
(中略)
filename = "list05.doc" # ワード文書の名前
premark = nil # 前の行の行頭文字をこれに記録
Wrdap.new(filename) do |doc|
tpl = doc.ListTemplates.Add # 新しいテンプレートを設ける
tpl.ListLevels(1).NumberFormat = "(%1)"
slc = doc.Application.Selection
pn = 1 # 段落番号
lines.each do |line| # 読み込んだテキストを1行づつ処理
mark = nil
if line =~ /^(・)\s*(.+)$/ # 箇条書きの場合
mark = $1
new_str = $2
if premark != mark # 前の行が箇条書きではない
doc.Paragraphs(pn).Range.ListFormat.ApplyListTemplate(
'ListTemplate'=>tpl, 'ContinuePreviousList'=>false)
end
slc.TypeText new_str
else # 箇条書きではない
if premark != nil # 前の行が箇条書きだった
doc.Paragraphs(pn).Range.Style = WdStyleNormal # 標準スタイルに
end
slc.TypeText line
end
premark = mark
slc.TypeParagraph() # エンターキーを押すのに相当
pn += 1 # 段落番号を1つ増やす
end
doc.save # ワード文書の保存
end
__END__
自前の箇条書きテンプレートを設けて文書を作ります。
楽器の種類
・弦楽器
・打楽器
・管楽器
おわり.
−−−−−−−− list05.rb ここまで
上のスクリプトではやっていませんが、新たに設けたテンプレートに名前を付けることができます。次のようにします。
tpl = doc.ListTemplates.Add
tpl.Name = "テンプレートA"
こうしておくと、この新しいテンプレートを次のように名前で呼び出すことができます。
doc.ListTemplates("テンプレートA")
ワード文書を保存すると、新たに設けたテンプレートも一緒に保存されます。次にその文書を開いた時に、同じテンプレートを利用することができます。
話は違いますが、先に「行頭文字形式」の行頭文字をunicodeで書くことができる旨を記しました。例えば、箇条書きのチェックマークは F0FC です。これをrubyでは “\uF0FC” と記述します。
この記述を用いる例を list05b.rb として同梱しておきます。新たなテンプレートを設けて、それをチェックマークの行頭文字形式にする例です。テンプレート設定に関わる部分は次のとおり。
tpl = doc.ListTemplates.Add # 新しいテンプレートを設ける
tpl.ListLevels(1).NumberFormat = "\uF0FC" # チェックマークに設定
tpl.ListLevels(1).NumberStyle = WdListNumberStyleBullet
tpl.ListLevels(1).Font.Name = "Wingdings"
上のように NumberFormat に "\u……" を代入することについて、あれこれ試してみると、うまくいくこともあれば駄目なこともあります。なぜかは分かりません。おそらく私が把握していない設定ルールがあるのだと思います。
また、行頭絵文字形式については、PictureBulletプロパティがInlineShapeオブジェクトに設定されている必要があります。自前で行うのは難しいような気がします。
行頭文字形式または行頭絵文字形式を確実に設定したい場合、なるべく既定のテンプレートを適用するのが無難だと思います。
既定のテンプレートのところで出てきた「アウトライン形式」は、9つの階層を持つ箇条書き形式です。
新たなテンプレートを設ける時に、Addメソッドに引数を渡すと、アウトライン形式のテンプレートを得ることができます。VBAの書き方とrubyの書き方を示してみます。
ActiveDocument.ListTemplates.Add(OutlineNumbered:=True)
doc.ListTemplates.Add('OutlineNumbered'=>true)
上のようにして取得したテンプレートは、アラビア数字形式であって、レベル1の書式は “%1”, レベル2の書式が “%1.%2”, レベル3は “%1.%2.%3” となっています。
これを好みの形に修正した上で適用するわけですが、とりあえず、修正せずにそのまま使う例を示します。
2つのレベルからなる箇条書きです。原稿のテキストにおいて、レベル2は、行頭に1つのタブを置いてあります。行頭にタブがなければレベル1です。
また、箇条書きであることを示すため、行頭に「・」を書いてあります。レベル2の場合はタブ1つの後に「・」がきます。
以下、list06.rb を掲げます。
−−−−−−−− list06.rb ここから
# encoding: Windows-31J
require "wrdap"
(中略)
filename = "list06.doc" # ワード文書の名前
premark = nil # 前の行の行頭文字をこれに記録
Wrdap.new(filename) do |doc|
tpl = doc.ListTemplates.Add('OutlineNumbered'=>true)
slc = doc.Application.Selection
pn = 1 # 段落番号
lines.each do |line| # 読み込んだテキストを1行づつ処理
mark = nil
if line =~ /^(\t*)(・)\s*(.+)$/ # 箇条書きの場合
tab = $1
mark = $2
new_str = $3
if premark != mark # 前の行が箇条書きではない
doc.Paragraphs(pn).Range.ListFormat.ApplyListTemplate(
'ListTemplate'=>tpl, 'ContinuePreviousList'=>false)
end
doc.Paragraphs(pn).Range.ListFormat.ListLevelNumber = tab.length+1
slc.TypeText new_str
else # 箇条書きではない
if premark != nil # 前の行が箇条書きだった
doc.Paragraphs(pn).Range.Style = WdStyleNormal # 標準スタイルに
end
slc.TypeText line
end
premark = mark
slc.TypeParagraph() # エンターキーを押すのに相当
pn += 1 # 段落番号を1つ増やす
end
doc.save # ワード文書の保存
end
__END__
アウトライン形式のテンプレートを設けて文書を作ります。
楽器
・弦楽器
・ヴァイオリン
・チェロ
・打楽器
・和太鼓
・タンバリン
・管楽器
・フルート
・クラリネット
おわり.
−−−−−−−− list06.rb ここまで
階層レベル1と2の違いを設定し分ける時のキーワードは ListLevelNumber です。
行頭のタブコードを変数 tab に代入していますが、tab.lengthが0であればタブがないことになるので、階層レベルが1です。tab.lengthが1ならタブが1つあるので、階層レベル2になります。
そして、ListLevelNumberに数値1を代入するとレベル1になり、2を代入すればレベル2になります。
「doc.Paragraphs(pn).Range.ListFormat.ListLevelNumber = tab.length+1」というのが、レベル設定の箇所です。
それから、新たに設けたテンプレートを変更したい場合ですが、
例えば、レベル1を「1. 2. ……」にして、レベル2を「■」にしたい時は次のようにします。
tpl = doc.ListTemplates.Add('OutlineNumbered'=>true)
tpl.ListLevels(1).NumberFormat = "%1."
tpl.ListLevels(2).NumberFormat = "■"
tpl.ListLevels(2).NumberStyle = WdListNumberStyleCardinalText
上では、NumberFormatを「■」に設定した後で、NumberStyleに「標準文字形式」のWdListNumberStyleCardinalText(数値6)を代入しています。
既定のテンプレートのところで出てきた「行頭文字形式」のWdListNumberStyleBullet(数値23)をNumberStyleに代入することもできますが、その場合は、NumberFormatよりも後ろにNumberStyleを持ってこないと、エラーが発生します。NumberFormatに「%1」などが含まれていると、「行頭文字形式」に設定できないのかもしれません。
それに対し、「標準文字形式」の場合は、NumberStyleをNumberFormatの前または後ろのどちらに置いてもエラーは起きません。だからというわけではありませんが、「● ■ ◆」のような文字を行頭マークとして使いたい時は、6の「標準文字形式」にする方が無難なような気がします。
「行頭文字形式」は、いわゆる「記号と特殊文字」の割り当てを意識しているのだと思います。
ここには掲げませんが、上のようにテンプレートを変更して適用する例を list06b.rb として同梱しておきます。
先の list05.rb, list06.rb では、ListTemplates.Addによって新たに設けた箇条書きテンプレートを必要に応じて適用しています。適用は、ApplyListTemplate() で行いました。
適用によって特定の段落に割り当てられたテンプレートは、Addメソッドで新たに設けたテンプレートそのものかというと、実は違います。新たに設けたテンプレートのコピーです。
ワード文書に帰属する箇条書きテンプレートの個数は、ActiveDocument.ListTemplates.Count で知ることができますが、ApplyListTemplate() を実行する前と後で個数を確認すると、適用後は個数が1つ増えます。
仮に、Addメソッドによって新たに設けたテンプレートを「素材テンプレート」、また、特定の段落のListFormat.ListTemplateを「個別テンプレート」と呼ぶことにすると、「素材テンプレート」と「個別テンプレート」は、中身は同じですが別物です。
なので、適用後に「個別テンプレート」を変更しても「素材テンプレート」の方は変更されません。
これは、ギャラリーの下にある既定のテンプレートを「素材テンプレート」として適用した時にも同じことがいえます。既定のテンプレートを適用すると、実は ActiveDocument.ListTemplates.Count が1つ増えます。
list06b.rbでは、「素材テンプレート」のレベル2のNumberFormatを「■」にしています。これを特定の段落(paragraph)に適用した後で、NumberFormatを「△」に変更したとしましょう。
このとき、「素材テンプレート」の方は「■」のままであり、「個別テンプレート」の方は「△」になります。
rubyスクリプトの要所のみ掲げると、次のとおりです。
tpl = doc.ListTemplates.Add('OutlineNumbered'=>true)
tpl.ListLevels(2).NumberFormat = "■"
(中略)
if premark != mark # 前の行が箇条書きではない
doc.Paragraphs(pn).Range.ListFormat.ApplyListTemplate(
'ListTemplate'=>tpl, 'ContinuePreviousList'=>false)
tpl2 = doc.Paragraphs(pn).Range.ListFormat.ListTemplate
tpl2.ListLevels(2).NumberFormat = "△"
end
上に出てくる tplが「素材テンプレート」、tpl2の方が「個別テンプレート」です。
このようなテンプレートのコピーが ApplyListTemplate() の実行の度に機械的に行われるのかというと、そうではないみたいです。一組の箇条書きには同じ「個別テンプレート」が割り当てられるようです。
何行か書き込まれてはいるものの、まだ箇条書きの設定を何も行っていないワード文書で、段落を1つづつたどりながら、該当する段落に箇条書きの設定を施すことを考えたとします。
ある段落が箇条書きの1行目に該当する場合は、それに対し ApplyListTemplate() を実行する時に、オプションの ContinuePreviousList を false にします。これまでの箇条書きの連続番号を継承しない(1番から新たに付け直す)ことを意味するオプションです。この場合、「素材テンプレート」のコピーである「個別テンプレート」が設けられるようです。
一方、ある段落が箇条書きの2行目以降に該当する場合は、ApplyListTemplate() を実行する時に ContinuePreviousList を true にしますが、この時はテンプレートのコピーが行われず、1つ前の段落に割り当てられている「個別テンプレート」がそのまま用いられるようです。
実は、ワードがどのようなルールによって暗黙のテンプレートコピーを行うのか、その詳細は分かりません。ContinuePreviousListオプションを引き合いに出しましたが、その値 false, true がコピーの有無を単純に左右するわけではないと思います。また、他にもコピーの有無に影響する要素はあるはずです。
ともあれ、段落に適用されている箇条書きのテンプレートは、ActiveDocument.ListTemplates(テンプレート群)に所属しているのは確かだと思います。
であれば、list01.rb(ローマ数字の箇条書きを丸付きの数字に変更)でやっているように、段落を1つづつたどって箇条書きの設定を変更する、というのではなく、ActiveDocument.ListTemplatesをたどって、ローマ数字を丸付きの数字に変更する、という方法でもいいことになります。それをやっているのが list01b.rb です。この方が効率はいいと思います。
私がワードの箇条書きについて確認したことは、これまで述べてきたもので概ね終了です。
以下では、まだ触れていない事柄で参考になりそうなものを補足的に記しておきます。
ListFormatの下には次のようなプロパティとメソッドがぶら下がっています。
上のListStringは、テンプレートのNumberFormatの%1とか%2に具体的な番号や文字を当てはめた結果を返すものです。このプロパティに値を代入することはできません。
上のApplyBulletDefaultは、ギャラリー1番(行頭文字形式)のテンプレート1番を適用するもののようです。
同じように、ApplyNumberDefaultはギャラリー2番のテンプレート1番、ApplyOutlineNumberDefaultはギャラリー3番のテンプレート1番を適用するものだと思います。
pandocで生成したワード文書について、多くを調べたわけではありませんが、私が経験した範囲のことについて記します。
pandocで生成したワード文書について、ActiveDocumentにぶら下がっているListTemplatesをみると、いずれも9つのレベルを持っています。
文書中に行頭文字形式の箇条書きがある時は、行頭文字形式のテンプレートをその箇条書きに適用しています。階層レベルが2つ以上ある箇条書きであっても、同じ1つのテンプレートを適用するようです。2種類の行頭文字を交互に使うようですが、これについては後述。
おもしろいのは行番号形式の箇条書きです。レベル1が「1. 2.」などのアラビア数字で、レベル2が「a. b.」などのアルファベットだとします。すると、例えば、レベル1にはテンプレート3、レベル2にはテンプレート5を適用する、というようにレベルによってテンプレートを切り替えています。
本来なら、1つのアウトライン形式のテンプレートを設定し(レベル1がアラビア数字、レベル2がアルファベットのものを設定する)、その1つを適用するのが本来の在り方だろうと思いますが、そうではないようです。
pandocの行番号形式のテンプレートは、9つのレベルすべてについて、NumberStyleとNumberFormatが共通です。正確には、レベルが違えば %1 が %2 になったり %3 になったりはしますが、アラビア数字なら全部アラビア数字、ローマ数字なら全レベルともローマ数字です。
このようにしておくと、箇条書きにローマ数字が出てきたら、どのレベルかを気にすることなく、ローマ数字のテンプレートを適用すればいいことになります。多様な箇条書きに対応する場合、こうしたpandocのような処理の方が対応しやすいのかもしれません。
pandocがいつも同じやり方を採っているのかどうかは分かりませんが、いくつかの文書を見たかぎりでは、上に書いたような形になっています。
pandocで行頭文字形式の箇条書き(行番号形式ではない)を生成すると、行頭文字として次の2種類が使われます。
上の2種類の記号を交互に使うようです。つまり、レベル1がビュレット、レベル2がダッシュ、3が再びビュレット、4はダッシュという具合です。ただし、「o」(アルファベット小文字のオー)が出てくることもあるようです。厳密なルールは分かりません。
ちなみに、U+2022 というのは、rubyでは “\u2022” と記述します。
箇条書きの出だし位置などの指定は、ListLevelにぶら下がっている NumberPosition, TabPosition, TextPosition というプロパティで設定します。いずれもポイント単位の数値で指定します。
なお、文字のサイズについていうと、pandocで生成されるワード文書の標準フォントサイズは 12ポイント、通常のワード文書の標準フォントサイズが 10.5ポイントです。
〜 以上 〜
Copyright (C) T. Yoshiizumi, 2014 All rights reserved.