【MATLAB】画像や表をWORDファイルに貼り付ける【自動化】

## はじめに
[前回](https://qiita.com/dokagui_tairan/items/16661e6376fc464e6364)は`matlab`から`excel`ファイルに出力する方法を**完全に理解**し，`excel`との和解を成し遂げた．今回は同様に`word`と和解したいと思う．

まず`matlab`での`word`の簡単な使い方を解説し，その後でサンプルプログラムを作成する．



## `matlab`での`word`使い方
自分が使う機能のみ簡単に解説する．
他に解説して欲しい機能がありましたらコメントで質問していただければ頑張ってググります．

### Word初期化
```matlab:
word = actxserver('Word.Application');
word.Visible = 1;
document = word.Documents.Open(filepath);% 既存のファイルを開く
% document = word.Documents.Add();% 新規作成
selection = word.Selection;
```
`Documents.Open`では既存のファイルを開き，`Documents.Add`はファイルを新規に作成する．
どちらかを使用する．

`word.Visible = 0`とすると，`word`が非表示となって裏で作業してくれるのだが，エラーで止まったりした時（画像ファイルのパスが間違っているなど）にどこで止まったのか分かりやすいので表示させている．


### 画像貼り付け

```matlab:
selection.InlineShapes.AddPicture(filepath);
```
`AddPicture`の引数には画像ファイルのフルパスを入れる．

### 文字挿入
```matlab:
selection.Font.Size = 11;
selection.Font.Name = 'ＭＳ　明朝'
selection.Paragraphs.Alignment = 'wdAlignParagraphCenter';  % センタリング
selection.TypeText(str);
```
`selection.TypeText(str);`で文字を挿入する．
`selection.Font`で文字サイズ等を指定する．

### 改行
```matlab:
selection.TypeParagraph;
```

### 表
```matlab:
% 表枠の作成
% 3行2列の表
table = selection.Tables.Add(selection.Range , 3, 2, 1);
table.Rows.Alignment = 'wdAlignRowCenter';  % 表をセンタリング

% 表の列幅を設定
% 設定しなくても良い．設定しないと列幅は自動で設定される．
col1 = 42.4;% mm　ここは自分で好きな値を入れる
col2 = 62.5;% mm
COL1WIDTH = col1 * 2.83465;% mm->ptに変換
COL2WIDTH = col2 * 2.83465;
table.Columns.Item(1).SetWidth(COL1WIDTH, 0);% 列幅をptで指定
table.Columns.Item(2).SetWidth(COL2WIDTH, 0);

% 表に値を入れていく
% 1行1列目
table.Cell(1,1).Select;
selection.TypeText("aaa");
% 1行2列目
table.Cell(1,2).Select;
selection.TypeText("bbb");

%...

% 表の選択解除
selection.MoveDown;
```


### 改ページ
```matlab:
selection.InsertBreak;
```

### ファイル保存
```matlab:
saveFileName = fullfile(path, 'test.docx');
document.SaveAs2(saveFileName);
```
`SaveAs2`の引数にはフルパスを入れる．

### ファイルクローズ
```matlab:
document.Close;
word.Quit();
```


## サンプルプログラム作成
前節で`matlab`の`word`の使い方を説明したので，次は実際にプログラムを作成してみる．

業務においては分析結果のbitmapや分析パラメタの表を`word`に貼り付けて報告書を作成することがある．この時，bitmap等の貼り付けは人力だったり，VBAマクロを使ったりしている．今回は`matlab`でbitmapや表の貼り付けを自動化したいと思う．

### 準備
実装方法は色々あると思うが，ここではテンプレートとなるワードファイルを準備し，`matlab`でそれを開いてbitmap等を追加していって別名で保存するという方式を取る．

下図のようなテンプレートファイルを用意した．`word`を開いて新規作成して，「挿入」から「ページ番号」を追加して，「word_template.docx」として保存した．
<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/3a1a04d6-5dda-7239-4001-f64c473868ab.png">

文字フォントや表の幅など詳しく指定したい場合は，完成形を1ページだけ手作業で作ってみて，フォントサイズだとか表の幅などの必要な情報をメモしておく．
<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/3bb47eea-765e-0a60-d6af-97789a3a1318.png">


### 貼り付ける画像を準備
カレントディレクトリとサブディレクトリ内のmp3ファイルを検索して，振幅グラフを描いて，結果をbitmapに貼り付ける処理を実装した．

[【MATLAB】指定フォルダ内のファイルを検索して処理をする](https://qiita.com/dokagui_tairan/items/e60ba14f0519397616a5)
を殆どそのまま持ってきただけなので解説は特にしない．

```matlab:
clc
clear all
close all

% カレントディレクトリとサブディレクトリを検索
fileList = dir('**/*.mp3');
for n = 1:length(fileList)   
    % 音声ファイルを開く
    filepath = fullfile(fileList(n).folder, fileList(n).name);
    [y, Fs] = audioread(filepath);
    if(size(y, 2) ~= 1)
        % 1chのみ取り出す
        y = y(:,1);
    end
    % 振幅グラフを描く
    t = ((1:length(y))-1)/Fs;
    h = figure;
    plot(t, y);
    [~,name,~] = fileparts(fileList(n).name);
    xlabel('t[s]');
    ylabel('振幅');
    title(name);
    
    % bitmapとして保存する
    bitmapname = sprintf("%s.bmp", name);
    filepath = fullfile(fileList(n).folder, bitmapname);
    saveas(h, filepath);
    close(h);
end
```

#### 作成した画像
作成した画像は以下のようになった．レポートとして添付する場合はもう少しグラフの見栄えを調整すべきであるが，本記事の趣旨とは逸れるので触れないでおく．
<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/b9d0b06d-2db4-259b-6875-575bd3d47e9e.png">

### `word`貼り付け処理の実装

```matlab:
clc
clear all
close all

% カレントディレクトリとサブディレクトリを検索
fileList = dir('**/*.mp3');
% Word貼り付けに使うデータを格納する
addWordFileList = cell(length(fileList), 3); 
for n = 1:length(fileList)   
    % 音声ファイルを開く
    filepath = fullfile(fileList(n).folder, fileList(n).name);
    [y, Fs] = audioread(filepath);
    if(size(y, 2) ~= 1)
        % 1chのみ取り出す
        y = y(:,1);
    end
    % 振幅グラフを描く
    t = ((1:length(y))-1)/Fs;
    h = figure;
    plot(t, y);
    [~,name,~] = fileparts(fileList(n).name);
    xlabel('t[s]');
    ylabel('振幅');
    title(name);
    
    % bitmapとして保存する
    bitmapname = sprintf("%s.bmp", name);
    filepath = fullfile(fileList(n).folder, bitmapname);
    saveas(h, filepath);
    close(h);
    
    % word貼り付けで使用する値を格納
    addWordFileList(n, 1) = {filepath};
    addWordFileList(n, 2) = {Fs};
    addWordFileList(n, 3) = {fileList(n).bytes};
end

% 各設定
% フォント
FONTSIZE = 11;
FONTNAME = 'ＭＳ　明朝';
% 列幅
col1 = 42.4;% mm
col2 = 62.5;



% word 初期化
word = actxserver('Word.Application');
word.Visible = 1;
path = pwd;
fp = fullfile(path, 'word_template.docx');
document = word.Documents.Open(fp);
% document = word.Documents.Add();
selection = word.Selection;

selection.Font.Size = FONTSIZE;
selection.Font.Name = FONTNAME;
COL1WIDTH = col1 * 2.83465;
COL2WIDTH = col2 * 2.83465;
    
% カレントディレクトリとサブディレクトリを検索
fileList = dir('**/*.bmp');

for n = 1:length(addWordFileList)
    bitmapFilePath = string(addWordFileList(n, 1));
    % 画像を貼り付け
    selection.InlineShapes.AddPicture(bitmapFilePath);
    
    % 改行
    selection.TypeParagraph;
    
    % 図タイトルを挿入
    selection.Paragraphs.Alignment = 'wdAlignParagraphCenter';  % センタリング
    [~,name,~] = fileparts(bitmapFilePath);
    str = sprintf("図%d　%s　振幅グラフ", n, name);
    selection.TypeText(str);
    selection.TypeParagraph;
    selection.TypeParagraph;
    
    % 表を貼り付け
    str = sprintf("表%d　%s　諸元一覧", n, name);
    selection.TypeText(str);
    selection.TypeParagraph;
    % 表枠の作成
    table = selection.Tables.Add(selection.Range , 3, 2, 1);
    table.Rows.Alignment = 'wdAlignRowCenter';  % センタリング
    % 表の列幅を設定
    table.Columns.Item(1).SetWidth(COL1WIDTH, 0);
    table.Columns.Item(2).SetWidth(COL2WIDTH, 0);
    % 表に値を入れていく
    table.Cell(1,1).Select;
    selection.TypeText("ファイル名");
    table.Cell(1,2).Select;
    [~,name,~] = fileparts(bitmapFilePath);
    selection.TypeText(name);
    table.Cell(2,1).Select;
    selection.TypeText("サンプリング周波数");
    table.Cell(2,2).Select;
    str = sprintf("%d", cell2mat(addWordFileList(n, 2)));
    selection.TypeText(str); 
    table.Cell(3,1).Select;
    selection.TypeText("ファイルサイズ"); 
    table.Cell(3,2).Select;
    str = sprintf("%d", cell2mat(addWordFileList(n, 3)));
    selection.TypeText(str);
    % 表の選択解除
    selection.MoveDown;
    
    selection.TypeParagraph;
       
    % 最後のファイル以外ならば改ページする
    if(n < length(fileList))
        selection.InsertBreak;
    end
end

d = pwd;
saveFileName = fullfile(d, 'output.docx');
document.SaveAs2(saveFileName);
word.Quit();
```

音声ファイルを開いて振幅グラフを描いてbitmap保存するループと，bitmapを`word`に貼り付けるループを別々に作ったが，もちろん一つのループで実装しても良い．というかそのほうが楽である．
別にループを作ったためにわざわざ`addWordFileList`というバッファを用意しなきゃならんかったからね．

### 結果
結果は下図のようになった．画像や表が期待通りに挿入されており，右上のページ番号もしっかりと表示されている．
![出力結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373168/334a4dc2-0cdf-3e63-16e5-f406a998536f.png)


## まとめ
今回は`word`との和解を成し遂げた．
これで`matlab`による業務の自動化が一段と進んだ．

しかしながら今回の和解は最低限度のレベルであり，章番号とかページ番号の割り振りとか，その辺の機能の理解がまだ出来ていない．
引き続き対話を続けていくものとする．