Word に複数の自作スタイルを一括登録する


便利な反面、登録が面倒極まりない Microsoft Word のスタイルを python で一括登録します。

## 実行イメージ
+ 文字単位のスタイルは背景色＋下線の種類で区別
+ 段落単位のスタイルは背景色＋囲み線の色で区別

![202005202190044.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501098/3bed543b-8f94-1364-9dc0-5774c9cef67f.png)

+ 表は外周の罫線の色で区別

![202005232210703.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501098/1e1e2f27-4077-8553-f8f0-1560c0e46da6.png)


## コード
[別の記事](https://qiita.com/AWtnb/items/52e206596b6440968833) で書いたように、pywin32 を使って現在開いている文書を見ながら処理します（Word VBA の定数は [こちらの記事](https://qiita.com/AWtnb/items/b2bcae6c9ab62b649b67) など参照）。

デフォルトだと「実行時点でカーソルがある段落の書式設定」を基準に新規スタイルが作成されます。冒頭にタイトルとして大きなフォントが指定されているような場合に困るので、「標準」スタイルの書式を基準にしました。

```activeword_set-style.py

import win32com.client

class vb:
    wdLineStyleSingle          = 1
    wdLineWidth050pt           = 4
    wdLineWidth025pt           = 2
    wdLineWidth150pt           = 12
    wdStyleNormal              = -1
    wdStyleTypeCharacter       = 2
    wdStyleTypeParagraphOnly   = 5
    wdStyleTypeTable           = 3
    wdUnderlineThick           = 6
    wdUnderlineDouble          = 3
    wdUnderlineWavyHeavy       = 27
    wdUnderlineDotDotDashHeavy = 26
    wdUnderlineDottedHeavy     = 20
    wdUnderlineDotDashHeavy    = 25
    wdUnderlineDashHeavy       = 23

def colorhex_to_int(colorcode):
    hex = colorcode[1:7]
    r = int(hex[0:2], 16)
    g = int(hex[2:4], 16)
    b = int(hex[4:6], 16)
    return r + g*256 + b*256*256

def add_marker_style(doc, base_style):
    print('creating new marker style...')
    marker_color_table = (
        [1, {"fill":"#f5ff3d", "border":"#1700c2"}],
        [2, {"fill":"#97ff57", "border":"#ff007b"}],
        [3, {"fill":"#5efffc", "border":"#ffaa00"}],
        [4, {"fill":"#ff91fa", "border":"#167335"}],
        [5, {"fill":"#ffca59", "border":"#2f5773"}],
        [6, {"fill":"#d6d6d6", "border":"#0f1c24"}],
    )
    for mkr in marker_color_table:
        marker_style_name = f"myMaker{mkr[0]}"
        try:
            marker_style = doc.Styles.Add(marker_style_name, vb.wdStyleTypeParagraphOnly)
            marker_style.ParagraphFormat = base_style.ParagraphFormat
            for i in (-4,-3,-2,-1):
                marker_style.ParagraphFormat.Borders(i).LineStyle = vb.wdLineStyleSingle
                marker_style.ParagraphFormat.Borders(i).LineWidth = vb.wdLineWidth050pt
                marker_style.ParagraphFormat.Borders(i).Color = colorhex_to_int(mkr[1]["border"])
            marker_style.Font = base_style.Font
            marker_style.NextParagraphStyle = base_style
            marker_style.ParagraphFormat.Shading.BackgroundPatternColor = colorhex_to_int(mkr[1]["fill"])
            marker_style.ParagraphFormat.OutlineLevel = mkr[0]
            marker_style.QuickStyle = True
            print(f' + "{marker_style_name}"')
        except:
            print(f'failed to create style "{marker_style_name}" ...')

def add_character_style(doc, base_style):
    print('creating new character style...')
    char_color_table = (
        [1, "#ffda0a",vb.wdUnderlineThick],
        [2, "#66bdcc",vb.wdUnderlineDotDashHeavy],
        [3, "#a3ff52",vb.wdUnderlineDottedHeavy],
        [4, "#ff7d95",vb.wdUnderlineDouble],
        [5, "#bf3de3",vb.wdUnderlineDashHeavy],
        [6, "#ff9500",vb.wdUnderlineWavyHeavy],
    )
    for char in char_color_table:
        char_style_name = f"myChar{char[0]}"
        try:
            char_style = doc.Styles.Add(char_style_name, vb.wdStyleTypeCharacter)
            char_style.Font = base_style.Font
            char_style.Font.Shading.BackgroundPatternColor = colorhex_to_int(char[1])
            char_style.Font.Color = colorhex_to_int("#111111")
            char_style.Font.Underline = char[2]
            char_style.QuickStyle = True
            print(f' + "{char_style_name}"')
        except:
            print(f'failed to create style "{char_style_name}" ...')

def add_table_style(doc, base_style):
    print(f'creating new table style...')
    border_color_table = (
        [1,"#2b70ba"],
        [2,"#fc035a"],
        [3,"#0d942a"],
        [4,"#ff4f14"],
        [5,"#fffb00"],
    )
    for tbl in border_color_table:
        table_style_name = f"myTable{tbl[0]}"
        try:
            table_style = doc.Styles.Add(table_style_name, vb.wdStyleTypeTable)
            table_style.Font = base_style.Font
            for i in (-4,-3,-2,-1):
                table_style.Table.Borders(i).LineStyle = vb.wdLineStyleSingle
                table_style.Table.Borders(i).LineWidth = vb.wdLineWidth150pt
                table_style.Table.Borders(i).Color = colorhex_to_int(tbl[1])
                table_style.Table.Shading.BackgroundPatternColor = colorhex_to_int("#eeeeee")
            print(f' + "{table_style_name}"')
        except:
            print(f'failed to create style "{table_style_name}" ...')

def main():
    wdApp = win32com.client.Dispatch("Word.Application")
    if wdApp.Documents.Count < 1:
        if not wdApp.Visible:
            wdApp.Quit()
        return 0

    doc = wdApp.ActiveDocument
    normalStyle = doc.Styles(vb.wdStyleNormal)

    add_marker_style(doc, normalStyle)
    add_character_style(doc, normalStyle)
    add_table_style(doc, normalStyle)

if __name__ == '__main__':
    main()
```

## powershell から呼び出す

メイン使用の powershell から下記のコマンドレットを作って呼び出しています。もちろん `python activeword_set-style.py` で直接呼び出しても構いません。

```powershell
function Set-MyStyleToActiveWordDocumentWithPython {
    if ((Get-Process | Where-Object ProcessName -EQ "winword").Count -lt 1) {
        return
    }
    $pyCodePath = "{0}\python_code\activeword_set-style.py" -f $PSScriptRoot
    'python -B "{0}"' -f $pyCodePath | Invoke-Expression
}
```