今回もワードファイルの処理です。前回と同じライブラリ(python-docx)を使用します。前回は1からワードファイルを作成しましたが、実際には既存のプレーンテキストから、それなりにフォーマットされた(形式化された)ワードファイルを生成したい場合が多いのではないでしょうか。ということで、今回はプレーンテキストを読み込んで、自動的に特定のフォーマットに揃えていくことにします。
ここらへんはマークダウンなどと併用した方がいいのですが、今回はシンプルに文字のサイズや書式だけをフォーマットすることにします。
なお、以後に説明するプログラムは上記のワードのドキュメントと同じ階層のディレクトリ(フォルダ)にあるものとします。もちろんプログラム内でワードファイルがあるファイルパスを指定すれば異なる場所にあっても問題ありません。
プレーンテキストを読み込む
まず、固定された場所(ファイルパス)にあるプレーンテキスト(標準テキスト)を読み込んでワードファイルにし保存するプログラムを作成してみます。
読み込むプレーンテキストは以下の図のようになっています。ファイル名はtemp.txtとしています。実行するPythonのプログラムと同じ階層のディレクトリにあることが前提になります。内容は前々回で使用した町内会の一斉清掃のお知らせとほぼ同じです。
テキストファイルを読み込むにはPythonではopen()を使います。open()のパラメーターに読み込むファイル名(ファイルパス)を指定します。この時にwith open()としておくと安心です。一般的なファイルの読み出し、書き込みなどの操作を行う場合、「ファイルを開く」「処理する」「ファイルを閉じる」という一連の流れになります。この時、処理が終わったのにファイルを閉じ忘れてしまうことがあります。with open()とすると、このファイルの閉じ忘れを防いでくれます。つまり、閉じ忘れてもPython側で自動的に閉じてくれます。こういうありがたい機能はなるべく使った方がいいでしょう。
ここまでの処理をまとめると以下の1行になります。ここでfとありますが、これはファイル操作を行うためのものです。fでなく他の文字列で構いません。
with open('temp.txt') as f:
さて、次にファイルの内容を読み込みます。まとめて一回で読み込ませてみます。丸ごと読み込むにはread()を使います。特に()内にパラメーターを書く必要はありません。 読み込んだテキストは新規に追加した段落内に読み込ませるので以下のようになります。
doc.add_paragraph(f.read())
これでできあがりです。実際のプログラムは以下のようになります。5行しかありませんので理解もしやすいでしょう。
import docx
doc = docx.Document()
with open('temp.txt') as f:
doc.add_paragraph(f.read())
doc.save("sample1.docx")
このプログラムを実行すると図のようになります。
これでできてしまえば話は簡単ですが、生成されたワードファイルの内容を見ると何か変です。もちろん、これで構わないという場合もあります。何が変なのかというと以下のプログラムを実行するとわかります。先ほどのプログラムの最後に段落の総数を出力する処理を末尾に追加しました。
import docx
doc = docx.Document()
with open('temp.txt') as f:
doc.add_paragraph(f.read())
doc.save("sample1.docx")
print(len(doc.paragraphs))
見た目の段落は複数あるのに段落は1つしかないと表示されています。段落ごとに生成するには1行ずつ読み込んで追加する、という処理を書かなければいけません。1行ごと読み込んで段落を追加して処理する場合は以下のようなプログラムになります。forで行数分だけ繰り返すように処理を変更しています。
import docx
doc = docx.Document()
with open('temp.txt') as f:
for p in f:
doc.add_paragraph(p)
doc.save("sample2.docx")
print(len(doc.paragraphs))
実行すると図のようになります。表示される段落総数が1ではなくなっているのがわかります。これで段落ごとの処理ができるようになります。
ところが今度は余計な改行まで入ってしまい2ページになってしまいました。
改行の削除と行揃え
今度は余計な行末の改行コードを削除します。これは簡単に実現できます。読み込んだテキストにrstrip()を実行するだけです。実際のプログラムは以下のようになります。 実行すると余計な改行がなくなり、ようやく期待した状態になりました。
import docx
doc = docx.Document()
with open('temp.txt') as f:
for p in f:
doc.add_paragraph(p.rstrip())
doc.save("sample3.docx")
ワードで特に装飾しないのであれば、これでもよいかもしれません。が、さすがに何も装飾しないのであればワードである必要性がありません。そこで次に行揃えを行ってみましょう。
普通は見た目よく(?)汎用性の高いプログラムを示す(作る)のが、このような講座では一般的ですが、ここではハードコーディングで示すことにします(汎用性ゼロ)。つまり、初心者がコピー&ペーストしてやってしまうアレです。欠点もありますが、上から下にベタがきなので場合によっては処理を追いやすいというメリットもあります。町内会のお知らせなどに限らず、だいたいこの系統の配布物はワンパターンなものが多いと思われるので割と、こんな方法でもいけるのかもしれません。ここらへん、プログラマだとどうしても気になってしまうところなので、マークアップ/マークダウン方式にするかなど思案してみるのもよいかもしれません。古いところではワープロの一太郎のようにテキストとスタイル属性等を別々のファイルとして持つようにして、処理するという方法もありでしょう。
さて、段落の行揃えは前回もやりましたので、そちらを参照してもらうとして段落のalignmentに行揃えの方向を示す値を入れます。
WD_ALIGN_PARAGRAPH.LEFT | 左揃え |
---|---|
WD_ALIGN_PARAGRAPH.CENTER | 中揃え(センタリング) |
WD_ALIGN_PARAGRAPH.RIGHT | 右揃え |
WD_ALIGN_PARAGRAPH.DISTRIBUTE | 両端揃え |
【公式ページ】WD_PARAGRAPH_ALIGNMENT
https://python-docx.readthedocs.io/en/latest/api/enum/WdAlignParagraph.html
段落ごとに行揃えを指定したプログラムは以下のようになります。最初の段落が0になるのでparagraphs[0]とすると最初の段落を操作できることになります。paragraphs[-1]のように負数を指定した場合は末尾からの段落を示します。つまりparagraphs[-1]は最後の段落を示すことになります。
プログラムを実行すると以下のようになります。期待通り行揃えが行われています。
import docx
from docx.enum.text import *
doc = docx.Document()
with open('temp.txt') as f:
for p in f:
doc.add_paragraph(p.rstrip())
doc.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[2].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[4].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[9].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.save("sample4.docx")
段落文字の装飾
次に文字の装飾を行ってみます。ここでは段落丸ごと同じ装飾を行います。まず、大見出しを太字(ボールド)にしてみます。文字の装飾関係は前回もやりましたので、そちらを参照してください。太字はboldにTrueを設定するだけです。
実際のプログラムは以下のようになります。実行すると大見出しが太字になっているのがわかります。
import docx
from docx.enum.text import *
doc = docx.Document()
with open('temp.txt') as f:
for p in f:
doc.add_paragraph(p.rstrip())
doc.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[2].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[4].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[9].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[4].runs[0].bold = True
doc.save("sample5.docx")
大見出しは太字になりましたので、今度は大見出しらしく文字サイズを変更し、より大きなサイズにしてみます。文字サイズはfont.sizeに値を設定します。この値はdocx.shared.Pt(18)のように指定します。これはポイント(pt)の単位で指定したことになります。つまり18ptの文字サイズになります。
実際のプログラムは以下のようになります。大見出しの段落が大きな文字になっているのがわかります。
import docx
from docx.enum.text import *
doc = docx.Document()
with open('temp.txt') as f:
for p in f:
doc.add_paragraph(p.rstrip())
doc.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[2].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[4].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[9].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[4].runs[0].bold = True
doc.paragraphs[4].runs[0].font.size = docx.shared.Pt(18)
doc.save("sample6.docx")
発行日をコマンドラインから渡す
ここまでのプログラムは特にコマンドラインから実行するメリットはありませんでした。コマンドラインから実行するメリットとしては任意の文字列を渡せるということです。
そこで今度はコマンドラインから日付を示す文字列が渡されたら最初の1行目(発行日)を置き換えてみます。なお、コマンドラインから日付を示す文字列が渡されなかった場合は、文字列を置き換えることなくそのままにしておくことにします。
まず、コマンドラインからの文字列を受け取るためにモジュールを読み込ませます。これまでと同様に以下の1行を追加します。
import sys
次にコマンドラインから日付文字列が渡されたかどうかを調べます。今回はコマンドラインからのパラメーターの総数を調べて判断することにします。パラメーターの総数は
len(sys.argv)
として求めることができます。なお、今回はパラメーターが1つだけなので総数を調べていますが、複数ある場合はエラー処理(try)を使った方がよいかもしれません。
まず、コマンドラインからの引数は実行するプログラムファイル名は必ず渡されます。つまりlen(sys.argv)が1より大きい場合は複数のパラメーターが渡されたことになります。
パラメーターが渡された場合はsys.argv[1]を、そのまま段落のテキストとして置き換えます。段落のテキストはtextに入っているので、ここにsys.argv[1]を入れると文字が置き換わります。
if len(sys.argv) > 1:
doc.paragraphs[0].text = sys.argv[1]
実際のプログラムは以下のようになります。図は文字列を指定した場合とそうでない場合の2パターンを示しています。
import sys
import docx
from docx.enum.text import *
doc = docx.Document()
with open('temp.txt') as f:
for p in f:
doc.add_paragraph(p.rstrip())
if len(sys.argv) > 1:
doc.paragraphs[0].text = sys.argv[1]
doc.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[2].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[4].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[9].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.paragraphs[-1].alignment = WD_ALIGN_PARAGRAPH.RIGHT
doc.paragraphs[4].runs[0].bold = True
doc.paragraphs[4].runs[0].font.size = docx.shared.Pt(18)
doc.save("sample7.docx")
画像を挿入する
配布物によっては地図などの図版が入る場合があります。そこで文書の最後に地図を追加してみます。地図の画像はmap.pngというファイル名で、実行するPythonプログラムと同じディレクトリ階層にあるものとします。
画像の追加は簡単でadd_picture()で()内に追加する画像のファイル名(ファイルパス)を指定するだけです。add_picture('map.png')とすればmap.png画像が追加されます。
実際のプログラムは以下のようになります。プログラムを実行すると文書の最後に地図が追加されます。
import sys