python-docxにおける下線・傍点・フリガナの処理

2020/11/28

[はじめに]

 ここでは python-docx において下線・傍点・フリガナを扱う方法
およびそのサンプルスクリプトについて記します。

 python-docxは、ワード文書(docxファイル)を処理するためのpythonライブラリです。

 docxファイルの中核をなすxmlを処理することによってファイルの生成や変更を行います。

 Windows上でワードを起動したりはしないので、 OSに依存せずpythonが動作する環境であれば使えます。

 下線については paragraph を構成するrunオブジェクトに対して
run.underline = True とすれば付加できますが、
点線の下線とか二重線の下線を指定することはできません。

 ここでは、そうした指定ができる方法について記します。

 python-docxは、その利用者がxmlのソースを意識することなく 簡単に処理できるよう工夫されていますが、その工夫の対象外の事柄を扱うときは xmlソースを意識した処理にならざるを得ません。

 なので、基本的な構成要素のxml表現を記しておきます。

 フリガナを扱うための構成要素は少々複雑なので後述します。


[目次へ]

1. 下線の付加

 まず下線にかかわるxmlソースについて述べ、
その後にpythonスクリプトを掲げます。

(1) 下線にかかわるxml

 下線にかかわるxmlは次の1行です。

<w:u w:val="single"/>

 上をrPrの中に盛り込みます。

 single を double にすれば二重下線になり、dot にすれば点線の下線になります。

 下線を含むrunのxmlソースを下に掲げておきます。

 「あいうえお」に下線が付く例です。

<w:r>
<w:rPr>
<w:u w:val="double"/>
</w:rPr>
<w:t>あいうえお</w:t>
</w:r>

(2) 下線を引くためのpythonスクリプト

 「名前は山内といいます。」というparagraphを生成しますが、
「山内」に下線を引きます。

 なので、paragraphは「名前は」 「山内」 「といいます。」の
三つのrunから構成されることになります。

 スクリプトでは、次の二つの関数を定義しています。

 以下、スクリプト underline.py です。

# encoding: utf-8
import docx
from docx.oxml import OxmlElement

# アンダーラインのrunを生成
def make_u_run(text, type = "single"):
    new_run = OxmlElement('w:r')
    rPr = OxmlElement('w:rPr')
    u = OxmlElement('w:u')
    u.set(docx.oxml.shared.qn('w:val'), type)
    rPr.append(u)
    new_run.append(rPr)
    new_run.text = text
    return new_run

# 通常のrunを生成
def make_normal_run(text):
    new_run = OxmlElement('w:r')
    new_run.text = text
    return new_run

## main
document = docx.Document()
document.add_paragraph('アンダーラインのテスト.')
p = document.add_paragraph()
new_run = make_normal_run("名前は")
p._p.append(new_run)
new_run = make_u_run("山内", "double")
p._p.append(new_run)
new_run = make_normal_run("といいます。")
p._p.append(new_run)
document.save('underline.docx')

(3) 下線の種類

 下線の種類は、私が把握した範囲では 17種類あります。

 下線を消去するための none を入れると18種類です。

 以下にその一覧を掲げます。

 xml値(xmlにおける書き方)のほかに、定数値と定数名も記します。

 定数値(整数)と定数名は VBAで用いるものですが、
拙作 usepandoc.py で下線の種類を指定するときは定数値を使います。

 たとえば下のとおり。

名前は#u3{山内}といいます。
xml値 定数値 定数名
none 0 wdUnderlineNone
single 1 wdUnderlineSingle
words 2 wdUnderlineWords
double 3 wdUnderlineDouble
dotted 4 wdUnderlineDotted
thick 6 wdUnderlineThick
dash 7 wdUnderlineDash
dotDash 9 wdUnderlineDotDash
dotDotDash 10 wdUnderlineDotDotDash
wave 11 wdUnderlineWavy
dottedHeavy 20 wdUnderlineDottedHeavy
dashedHeavy 23 wdUnderlineDashHeavy
dashDotHeavy 25 wdUnderlineDotDashHeavy
dashDotDotHeavy 26 wdUnderlineDotDotDashHeavy
wavyHeavy 27 wdUnderlineWavyHeavy
dashLong 39 wdUnderlineDashLong
wavyDouble 43 wdUnderlineWavyDouble
dashLongHeavy 55 wdUnderlineDashLongHeavy

[目次へ]

2. 傍点の付加

 傍点を付ける方法は、基本的に下線の場合と同じです。

 xmlの表現でいうと下のとおり。

<w:em w:val="dot"/>

 下線の w:uw:em に変更すれば傍点の指定になります。

 なので、xmlソースに関する説明は省略して、
スクリプトと、指定できる傍点の種類について記します。

(1) 傍点を付けるためのpythonスクリプト

 「今日の天気は雨天でした。」というparagraphを生成しますが、
「雨天」に傍点を付けます。

 「雨天」とその前後で分かれるので、合計三つのrunになります。

 以下、スクリプト EmphasisMark.py です。

# (encoding: utf-8)
import docx
from docx.oxml import OxmlElement

# 傍点のrunを生成
def make_em_run(text, type = "dot"):
    new_run = OxmlElement('w:r')
    rPr = OxmlElement('w:rPr')
    em = OxmlElement('w:em')
    em.set(docx.oxml.shared.qn('w:val'), type)
    rPr.append(em)
    new_run.append(rPr)
    new_run.text = text
    return new_run

# 通常のrunを生成
def make_normal_run(text):
    new_run = OxmlElement('w:r')
    new_run.text = text
    return new_run

## main
document = docx.Document()
document.add_paragraph('傍点のテスト.')
p = document.add_paragraph()
new_run = make_normal_run("今日の天気は")
p._p.append(new_run)
new_run = make_em_run("雨天", "circle")
p._p.append(new_run)
new_run = make_normal_run("でした。")
p._p.append(new_run)
document.save('EmphasisMark.docx')

(2) 傍点の種類

 傍点の種類は、私が把握した範囲では 4種類あります。

 傍点を消去するための none を入れると5種類です。

 以下にその一覧を掲げます。

xml値 定数値 定数名
none 0 wdEmphasisMarkNone
dot 1 wdEmphasisMarkOverSolidCircle
comma 2 wdEmphasisMarkOverComma
circle 3 wdEmphasisMarkOverWhiteCircle
underDot 4 wdEmphasisMarkUnderSolidCircle

[目次へ]

3. フリガナの付加

 まずフリガナにかかわるxmlソースについて述べ、
その後にpythonスクリプトを掲げます。

 ちなみに、xmlではフリガナを ruby(ルビ)と表現し、
VBAでは PhoneticGuide と表現するようです。

(1) フリガナにかかわるxml

 「山内」に対して「ヤマノウチ」というフリガナを付ける場合を考えます。

 構成要素の小さい方から述べます。

 まず「山内」という漢字本体を格納したrunを用意します。

 それとは別に「ヤマノウチ」というフリガナを格納したrunも用意します。

 次に、漢字本体のrunを rubyBase という要素に格納し、
フリガナのrunは rt という要素に格納します。

 そして、rubyという要素に rt および rubyBase を格納します。

 漢字本体よりフリガナの方を先に配置するのが標準のようです。

 最後に、rubyという要素を大きなrunに格納します。

 この大きなrunが paragraph の構成要素になります。

 xmlソースの形で示すと次のとおり。

<w:r>
<w:ruby>
    <w:rt>
        <w:r>
        <w:t>ヤマノウチ</w:t>
        </w:r>
    </w:rt>
    <w:rubyBase>
        <w:r>
        <w:t>山内</w:t>
        </w:r>
    </w:rubyBase>
</w:ruby>
</w:r>

 インデントをつけているのは、各要素の含む・含まれるの関係を示すためです。

 元のxmlソースには余分なスペースも改行もなくだらだら連続しています。

 また、上は最も単純化したxmlソースです。

 フリガナなどにプロパティを付けていません。

 なので、デフォルトのプロパティが適用されるとおもいます。

(2) フリガナを付けるためのpythonスクリプト

 「名前は山内といいます。」というparagraphを生成します。

 「山内」に「ヤマノウチ」というフリガナを付けます。

 以下、スクリプト ruby.py です。

# (encoding: utf-8)
import docx
from docx.oxml import OxmlElement

# フリガナ設定したrunを生成
def make_ruby_run(baseText, rubyText):
    base_run = OxmlElement('w:r')
    ruby_run = OxmlElement('w:r')
    base_run.text = baseText
    ruby_run.text = rubyText
    ruby = OxmlElement('w:ruby')  # 本体&フリガナの入れ物
    rt = OxmlElement('w:rt')  # フリガナの入れ物
    rt.append(ruby_run)
    rubyBase = OxmlElement('w:rubyBase')  # 本体の入れ物
    rubyBase.append(base_run)
    ruby.append(rt)
    ruby.append(rubyBase)
    new_run = OxmlElement('w:r')
    new_run.append(ruby)
    return new_run

# 通常のrunを生成
def make_normal_run(text):
    new_run = OxmlElement('w:r')
    new_run.text = text
    return new_run

## main
document = docx.Document()
document.add_paragraph('フリガナのテスト.')
p = document.add_paragraph()
new_run = make_normal_run("名前は")
p._p.append(new_run)
new_run = make_ruby_run("山内", "ヤマノウチ")
p._p.append(new_run)
new_run = make_normal_run("といいます。")
p._p.append(new_run)
document.save('ruby.docx')

    

~ 以上 ~

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