LaTeX文書に任意のルビを一括で割り当てるPythonプログラム

# はじめに
LaTeXで論文等を書く機会が増えた大学生です。難しい漢字や専門用語にルビを振る方法が分からなかったので、いろいろ調べてみました。

## 1.LaTeXでルビを振る方法
今回は okumacro パッケージを利用します
```LaTeX
% パッケージの利用
\usepackage{okumacro}
% 単語にルビを振る
\ruby{単語}{ルビ}
```

#### サンプルコード

具体的な使用方法を確認してみましょう
```LaTeX: okumakuroパッケージを利用したLaTeX文書
\documentclass{jsarticle}
\usepackage{okumacro}
\begin{document}

\ruby{競馬}{けいば}とは、指定されたコースを速く走り抜ける競走馬のレースです。

\end{document}
```

コンパイルすると以下のように表示されます⇩

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342063/6a33178b-b8d7-2603-9352-60cc253ac021.png)

## 2.既存のLaTeX文書にルビを付ける
基本的なルビの振り方は理解できました。
しかし既存の文書にルビを振りたい場合、文書中の単語一つ一つを修正していくのはかな～り面倒です(-_-;)
そこで！正規表現を利用して一括変換しましょう

## 2.1 Pythonで正規表現を利用した文字列の一括変換
reモジュールのsub()関数を用いて、ルビを振りたい単語を一括変換します

https://docs.python.org/ja/3/library/re.html#re.sub

## 2.2 wordlistを用意して、複数の単語にルビを振る
wordlist.csv を用意して、複数の単語とそのルビを用意します
1列目には単語、2列目には単語に付けたいルビを記入します

<img width="350" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342063/e90265f1-6a45-1344-6d9e-723e0fc4f12b.png">


（テキストエディタで開くと、カンマ区切りになっています）
```csvs: wordlist.csv
火山岩,かざんがん
火成岩,かせいがん
深成岩,しんせいがん
```

## 2.3 実際に置換する
### 2.3.1 以下の3ファイルを同じディレクトリに置いてください
- wordlist.csv(上記)
- sample.tex
```LaTeX: sample.tex
\documentclass{jsarticle}
\usepackage{okumacro}
\begin{document}

\section{サンプル}

マグマが冷え固まってできた岩石を、 \textbf{火成岩}といいます。 
火成岩は、さらに\textbf{火山岩}と\textbf{深成岩}に分けられます。 

\begin{enumerate}
  \item[a] \textbf{火山岩}とは、マグマが地表および地表付近で急激に冷やされてできた岩石です。 
  火山岩には、流紋岩、安山岩、玄武岩という種類があります。 
  \item[b] \textbf{深成岩}とは、マグマが地下深くでゆっくりと冷やされてできた岩石です。 
  深成岩には、花こう岩、せん緑岩、はんれい岩という種類があります。  
\end{enumerate}

\end{document}
```
    
 ※ コンパイルすると、以下のようになります
<img width="500" alt="image2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342063/462b4bb7-5bd3-2c47-d4e8-5e535fb37c27.png">



- Ruby2LaTeX.py
```Python: Ruby2LaTeX.py
import os
import re
import shutil
import csv
import chardet
import sys

def rename_file_extension(original_file, new_extension):
    base = os.path.splitext(original_file)[0]
    new_file = f"{base}{new_extension}"
    shutil.copy(original_file, new_file)
    return new_file

def detect_encoding(file_path):
    with open(file_path, 'rb') as file:
        raw_data = file.read()
        result = chardet.detect(raw_data)
        return result['encoding'], raw_data

def load_replacements(csv_file_path):
    replacements = {}
    with open(csv_file_path, encoding='utf-8') as csv_file:
        reader = csv.reader(csv_file)
        for row in reader:
            if len(row) >= 2:
                key, value = row[0], row[1]
                replacements[key] = f'\\\\ruby{{{key}}}{{{value}}}'  # 修正された部分
    return replacements

def add_ruby_to_text(file_path, output_path, replacements):
    encoding, raw_content = detect_encoding(file_path)
    content = raw_content.decode(encoding)
    
    for key, value in replacements.items():
        content = re.sub(key, value, content)
    
    with open(output_path, 'w', encoding=encoding, newline='') as output_file:
        output_file.write(content)

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("Usage: python Ruby2Latex_re.py <filename.tex>")
        sys.exit(1)
    
    input_tex_file = sys.argv[1]
    base_name = os.path.splitext(input_tex_file)[0]
    
    # 入力ファイルの拡張子を.txtに変更
    input_txt_file = rename_file_extension(input_tex_file, '.txt')
    
    # 置換を実行し、結果をAAA_rubied.txtに保存
    output_txt_file = f'{base_name}_rubied.txt'
    replacements = load_replacements('wordlist.csv')
    add_ruby_to_text(input_txt_file, output_txt_file, replacements)
    
    # 出力ファイルの拡張子を.texに変更
    output_tex_file = rename_file_extension(output_txt_file, '.tex')
```

### 2.3.2 実行
コマンドプロンプトから、以下のように実行して下さい
```
python Ruby2LaTeX.py sample.tex
```
実行すると、「sample_rubied.tex」というファイルが生成されるはずです
（第二引数にルビを振りたいファイル「XXX.tex」を指定すると、「XXX_rubied.tex」という新たなファイルが生成されます）

sample_rubied.tex をコンパイルしてみると...
<img width="500" alt="image3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3342063/b247c205-7492-416f-1cf3-4fd79b14e941.png">

無事にルビが振られました🎉

## 参考記事
- okumacro パッケージ
    - https://zrbabbler.hatenablog.com/entry/20151125/1448411386
- 正規表現
    - https://docs.python.org/ja/3/library/re.html#re.sub
    - https://note.nkmk.me/python-str-replace-translate-re-sub/#resub-resubn