pandocとpythonの組み合わせによるWord文書の作成

2020/11/28

[更新情報]

 2019/04/12に公開してから、しばらくぶりに更新しました。

 まず、WordRemake というオプション名を RemakeAfter に変更しました。

 pandocでdocxファイルを生成した後で、python-docxで更に調整を加えるかどうかを指定するものです。

 それから、こちらが主な更新点ですが、docxファイルへの変換において 下線・傍点・フリガナに対応しました。

 拙作 makeDOCX と同じ記述方法で下線・傍点・フリガナを付加できます。

 詳細は 8. 下線・傍点・フリガナの付加 を参照。

 また、下線・傍点・フリガナのテクニカルな情報については
python-docxにおける下線・傍点・フリガナの処理 を参照。


[目次へ]

[はじめに]

 pandocでWord文書(docxファイル)を生成した後で、
それに修正を加えたいことがしばしばあります。

 Wordを起動してその修正を行うとなると、
当然ながらWindows&Wordの環境が必要になります。

 それがどうにも気に入らない。なるべくOfficeソフトを使いたくない派なので。

 ということで、python-docxを利用してdocxファイルの修正をしようと考えました。

 そこで作成したのが usepandoc.py です。

 以下では pandoc, python がインストール済みであり、
どのディレクトリからでも実行可能であることを前提にします。

 処理する markdown原稿の書き方は、拙作 makeDOCX の場合と同じです。

 pandoc用オプション、Word修正用オプションをmarkdown原稿中に書き入れます。

※参考サイト:

目次へ戻る

    

1. 動作に必要なソフトウェア

 用いたpandocは ver 2.11.1.1 です。

 python ver 3.7.6 で動作確認しました。

 インストールが必要なpythonライブラリは次の二つ。

python -m pip install --upgrade python-docx  [enter]
python -m pip install --upgrade chardet  [enter]

 上のようにするとライブラリをインストールできます。

 付随して必要になるライブラリ(lxmlなど)もインストールされます。

目次へ戻る

    

2. とりあえず使うには

usepandoc.zipをダウンロードしてから解凍します。

すると usepandoc.py, test.txt などが出てくるので次のように実行します。

python usepandoc.py test.txt  [enter]

 実行すると test.docx が作成されるはずです。

 処理するファイルの名前にはワイルドカードも使えます。

 markdown原稿の test.txt は、utf-8 で書かれていますが、
別のエンコードでも大丈夫だとおもいます。

 日本語版Windowsの下では cp932(Shift_JIS)、
他の環境なら utf-8 で書いておくのが無難ではあります。

目次へ戻る

    

3. usepandoc.py のコマンドラインオプション指定

 usepandoc.py に指定するコマンドラインオプションは
--ext の一つだけです。

 --ext=html とすれば htmlファイルを生成します。

 --ext=pptx なら pptxファイルの生成。

 省略時は --ext=docx とみなされます。

 html, pptx の場合は、pandoc でファイルを生成したらそれで終了です。

 usepandoc.py が独自の修正を加えることはありません。

 なお、--ext 以外のマイナス記号で始まるオプションは、
pandocにそのまま引き渡されます。

*例: python usepandoc.py --ext=html --toc --mathml test.txt [enter]

目次へ戻る

    

4. サブディレクトリpandocに入っているもの

 usepandoc.zipを解凍すると pandoc というサブディレクトリが出てきます。

 その下には reference.docx, reference_pn.docx というファイル、
それから templates というディレクトリがあります。

 reference.docx は、Wordファイルを作成するときに
pandocが参照するスタイル定義ファイルです。

 これは、デフォルト版に少し変更を加えたものです。

 見出しが青色に設定されているのを色なしにするなどの変更です。

 reference_pn.docx は、フッターにページ番号を付加したもので
それ以外は reference.docx と同じです。

 必要に応じて好みの形に修正してください。

 templates というサブディレクトリには template01.html があります。

 これはデフォルトのテンプレートに次の手を加えたものです。

 template01.html を利用するときは、markdownの原稿に下の行を書き加えます。

<!-- pandoc --template=template01.html -->

 pandocにはデータディレクトリというのがあり、そこに reference.docx などがあるものと仮定されます。

 usepandoc.py ではそのデータディレクトリを直下の pandoc というサブディレクトリに変更しています(--data-dir オプションで変更)。

 もし pandocというサブディレクトリが存在しなければ
データディレクトリを変更はしませんが、
usepandoc.py をちゃんと機能させるためには残すことをお勧めします。

 usepandoc.py が存在するのと同じディレクトリに
pandoc というサブディレクトリを置くようにします。

目次へ戻る

    

5. markdown原稿中へのpandoc用オプションの書き込み

 pandoc用のmarkdownの原稿では下の行が覚書のメモとして無視されます(返還後の文書には表面に出てきません)。

<!-- ……… -->

 これは html のコメントの書き方です。

 このコメントの形で pandoc用のオプションを書き入れることができます。

<!-- pandoc --toc --mathml -->

 上のように書いておくと --toc および --mathml を指定することになります。

 <!-- は、前に空白を置かず必ず行頭から書いてください。

 tocは目次の生成、mathmlは数式の記述を有効にするオプションです。

 このコメント形式のオプションは、ファイル中のどこに書いてもかまいません。

 ただし、% で始まる冒頭のタイトルブロックよりは後ろに書きます。

 このコメントを利用したオプション指定は usepandoc.py に特有の書き方です。

 より一般的には yamlブロックを使ってオプションを指定する方法があります。

 それについては pandocのマニュアルなどを参照してください。

目次へ戻る

    

6. docxファイルを調整するためのオプション

 usepandoc.pyは、pandocで変換した後に python-docxを利用して変更を加えます。

 どんな変更を加えるかを示しつつ、変更の在り方をコントロールする方法を記します。

 なお、拙作 makeDOCX で行っている CharsLine(1行あたりの文字数)、LinesPage(1ページあたりの行数)には対応していません。

 また、箇条書きの行頭文字について、小文字ローマ数字の ‘i.’ を①, ②などの丸囲み数字に変更するという処理も行いません。

(1) ページのサイズに関する調整

 pandocが生成するのは letterサイズですが、それをA4サイズに変更します。

 <!-- PageSize=A5 --> という 1行を書いておけば
A5サイズに変更できます。

 指定できるサイズは次のとおり。

 B4, A4, B5, A5, HG(ハガキのサイズ)

 横長にしたいときは A4L のようにアルファベットのエルを付けます。

 横長を意味する landscape の頭文字のつもり。

: <!-- PageSize=B5L -->

(2) ページ番号

 各ページのフッターに 1/5 とか 4/5 のようなページ番号をつけます。

 「該当ページの番号、スラッシュ記号、総ページ数」の形式で
中央揃えでページ番号が付けられます。

 つけない場合は PageNumber=no を指定します。

 PageNumber=yes だとページ番号が付きます。

 yes, no は小文字で書いてください。

 ハガキ印刷用文書だとページ番号が不要です。

 また、後で自分なりにページ番号をデザインしたいときも番号がない方がやりやすいとおもいます。

: <!-- PageNumber=no PageSize=HG -->

 上のように複数のWord用オプションを 1行にまとめて書くことができます。

 ただし、pandoc用のオプションとは混在させないでください。

(3) 数値を指定するフォントサイズ、段組数

 数値を指定するオプションには次の二つがあります。

  1. FontSize: 標準フォントサイズ。指定しないと12ポイント。
    FontSize=10.5 のように指定。
  2. TextColumns: 段組みの数。TextColumns=2 とすれば2段組みになる。

(4) 表に関する調整

 以下に掲げる表の調整は、文書中に複数の表がある場合、
すべての表に一括して適用されます。

a. 罫線

 pandocで生成される表では、1行目と 2行目の間に罫線が引かれます。
でも、それ以外の罫線はありません。

 そこで、外枠を二重線、内側を普通の実線にします。

<!-- TableBorder=no -->

 上の行がファイル中にあると、罫線は引かれません。

b. 中央揃え

 表がページ内の左右バランスにおいて中央に位置するよう調整します。

 表にキャプションがあるときは、それも中央揃えにします。

<!-- TableCenter=no -->

 上の行があると、中央揃えにはなりません。

c. 1行目を均等割り付け

 表の 1行目の各セルの文字配置を均等割り付けにします。

 TableRow1=no を書いておくと、均等割り付けになりません。

(5) 画像の中央揃え

 画像をページ内の左右バランスにおいて中央揃えにします。

 画像のキャプションがあるときは、それも中央揃えにします。

 ただし、画像が独立した段落(パラグラフ)でない場合、
たとえば、文章と一緒になって段落を構成している場合は
中央揃えになりません。

 ImageCenter=no を指定すると、画像を中央揃えにしません。

(6) python-docxによる処理の抑制

 pandocの処理結果そのままを得たいときは RemakeAfter=no を指定します。

 これがあると python-docxによる変更は行われません。

 pandocでdocxを生成したらそれで終了です。

 結果として、Wordに関するオプションが書かれていても、それらは生かされません。

(7) Word用オプション一覧

 Word用オプションをそのデフォルト値とともに記します。

目次へ戻る

    

7. html生成時のID属性付加

 コマンドラインオプションの --ext=html を指定して
htmlファイルを生成する場合、見出しにID属性を付加します。

# 更新情報

 上のmarkdownの見出しの記述が pandocでは下の html記述に変換されます。

<h1 ID="更新情報">更新情報</h1>

 要するに ID属性がマルチバイト文字になります。

 最近のブラウザではマルチバイト文字でも問題なさそうですが、
半角英数文字の方が無難そうです。

# 更新情報 {#update}

 上のmarkdown記述であれば ID属性が update になります。

 usepandoc.py では ID属性の記述がない見出しに、機械的にIDを付加します。

 {#session-ex1} のような記述を付け加えます。

 見出しのレベルに関係なく、見出しの出現順に通し番号を割り当てます。

 ただし、次の二つのケースでは ID属性を付加しません。

 なお、属性の記述の {-}{#session-ex1 .unnumbered} のようになります。

目次へ戻る

    

8. 下線・傍点・フリガナの付加

 docxファイルへの変換において 下線・傍点・フリガナに対応させました。

 下線または傍点の場合、‘{’ の前に半角数字を1文字書くと、その値が
VBAでいうところの下線プロパティ、傍点プロパティにセットされます。

 #u3{Hello} のように3を書くと、二重下線になり、
#u4{Hello} のように4を書くと、点線の下線になります。

 数字がないときは、下線でも傍点でも 1 が指定されたものとみなされます。

 また、プロパティとして該当しない数字が指定されたときも
1 が指定されたものとみなされます。

 どの数字がどの下線・傍点に対応するのかは
下の定数を参考にしてください。

注意事項

 #u{ …… } などの記述の { …… } の中に
半角の {} を書くことはできないのでご注意ください。

 #e{#u{ …… }} のように二重に指定することは可能です。

 処理の順番として、下線・傍点の後にフリガナを処理します。

 なので、下線・傍点とフリガナを併用するときは
#r{#u{山内}:ヤマノウチ} のように下線・傍点の記述を内側にして下さい。

 フリガナを斜体にしようとして #r{山内:*ヤマノウチ*} のように書いても
残念ながら斜体にはなりません。フリガナのプロパティは指定不可です。

 フリガナのところに半角の記号類は書かないようにして下さい。

 なお、下線・傍点・フリガナのテクニカルな情報については python-docxにおける下線・傍点・フリガナの処理 を参照してください。


[目次へ]

9. プログラミング備忘録

 python-docxを利用していて少々手こずった点などを記します。

(1) tableのcellに罫線を付加する方法

 usepandoc.py の中に set_cell_border() という関数があります。

 表(table)の各セルに罫線を付加するための関数です。

 これは、下のサイトに掲げられていたものを使わせてもらいました。

How to setup cell borders with python-docx - Stack Overflow

 使い方については上記サイトか usepandoc.py の中身を参照してください。

 罫線の種類には single, double, dotDotDash を指定可能なようです。

 usepandoc.py では、外枠をdouble、内線をsingleにしてあります。

    

(2) pandocが生成するdocxのtable

 pandocが生成するtableを確認すると、ちょっと変則的です。

 tableオブジェクトが変数 table に代入されている場合

row_count = len(table.rows)
column_count = len(table.columns)

 上のようにして行数と列数を得られるはずですが、column_count が 0 になります。

 行数の方は適切な値が得られるのですが……

 table.cell(0, 0) が左上端のセルを示し、
table.cell(row_count-1, column_count-1) が右下端を示すはずなのに
そうは問屋がおろさない。

table.cell(0, 0),  table.cell(0, 1),  table.cell(0, 2), ……

 上のように行番号を0に固定し、列番号を徐々に挙げていって
どこでエラーが発生するかをチェックします。

 そうすると table に含まれるセルの個数を知ることができます。

 個数が分かれば何とか処理可能です。

 こういう表(table)でも Wordで問題なく表として扱えるんですね。

    

(3) ページ番号の付加

 python-docxを利用してフッターにページ番号を付加する方法を調べましたが、
結局、わかりませんでした。

 docxファイルを展開したとき、document.xml のほかに
footer1.xml のようなファイルが必要になるようですが、
それを生成する機能が python-docx にはまだないのでは?とおもいます。

 ということで、ページ番号を付加したテンプレート reference_pn.docx を用意して、それを使うことにしました。

 デフォルトの PageNumber=yes だと reference_pn.docx を参照し、
PageNumber=no なら reference.docx を参照します。

    

(4) 画像のチェック

 パラグラフに、画像だけでなく文章等もある場合と
画像だけの場合とがあります。

 ImageCenter=yes が指定されたとき、画像だけなら中央揃えにしますが
そうでなければ中央揃えにしません。

 この画像の中央揃えの処理は次のようにしました。

inline_tag = './w:drawing/wp:inline'
for para in document.paragraphs:
    if para.text == "" and len(para.runs) == 1 and \
        len(para.runs[0].element.xpath(inline_tag)) > 0:
        para.alignment = WD_ALIGN_PARAGRAPH.CENTER

    

(5) 段組みの設定

 段組みを設定するためのメソッドが特に用意されているわけではないので
xmlに手を加える方法をとりました。

 該当の処理は次のとおり。

section = document.sections[0]
sectPr = section._sectPr
cols = sectPr.xpath('./w:cols')[0]
cols.set(qn('w:num'), '{}'.format(optdct['TextColumns']))
cols.set(qn('w:sep'), '1')  # separate line: 0 or 1
cols.set(qn('w:equalWidth'), '1')  # equal width: 0 or 1

 段組みの境界線をつけ、均等幅にするようにしてありますが、
そうしたくない場合は該当箇所を書き換えてください。

    

~ 以上 ~

Copyright (C) T. Yoshiizumi, 2019-2020 All rights reserved.