Win32::OLEを使ったMicrosoft Wordの操作


Perl Advent Calendar 2019 の12日目です。
昨日はcodehexさんでした。

きっかけ

 紙文化の職場のため、決裁書類や契約書類など様々な定型書類に囲まれています。
 共通する内容を複数の書類に転記する必要があり、ミスの原因にもなりますし、時間が掛かっていました。
 そこで、入力フォームを作成してボタン一つで作成可能にするなど、業務量の圧縮を図りました。
 その際の知見を共有できたら良いなと感じましたので投稿します。

前提等

 現在、VBAを使っている人にはおすすめできると思います。
 Windows7とStrawberry Perl 5.30(いずれも32bit版)で行いました。

Win32::OLE

  • 良いところ
    • VBAで叩きを作っておけば、それをperlに書き直すだけ
    • Officeのマクロの記録機能を使用するとVBAコードが作成されるので、時間短縮が図れる
    • 定数などもVBAのイミディエイトウインドウで簡単に調べられるので良い(後述)
  • 悪いところ
    • 文字コードを常に意識する必要がある(CP932に変換しないとうまく動作しない)
    • VBAに慣れていると引数がはまりどころになる(後述)

Microsoft Office VBAでの定数の調べ方

 イミディエイトウインドウでTrueやFalse等の定数を調べることができます。

 図では、True = -1, False = 0, wdSendToNewDocument = 0 だと判ります。

引数について

 関数への引数はハッシュのリファレンスで渡します。
 下図の赤枠はWordのマクロ記録で作成されたVBAです。Tドライブのサンプル文書というファイルを開いたものです。

 この場合perlでは以下のようになります。

sample1.pl
# 引数準備
my %openparam;  
$openparam{"FileName"} = Encode::encode("CP932","t:\\サンプル文書.docx");
$openparam{"ReadOnly"} = 0; # False

# ファイルを開く
my $doc = $word->Documents->Open(\%openparam);  # 引数のハッシュはリファレンスで渡す

もしくは

sample2.pl
my $openparam;
$openparam->{"FileName"} = Encode::encode("CP932","t:\\サンプル文書.docx");
$openparam->{"ReadOnly"} = 0; # False

# ファイルを開く
my $doc = $word->Documents->Open($openparam);

例)Wordファイルを開き、文書を差し込み、ファイルを保存する

 例は、以下の5つを行っています。
1. 〔サンプル文書.docx〕を開く
2. 差込元データとして〔サンプル差込データ.xlsx〕のsheet1を指定する
3. 差し込みされた新規文書を作成する
4. 新規文書を印刷する
5. 新規文書を〔サンプル保存.docx〕として保存する

sample.pl
#!perl
use strict;

use Win32::OLE;
use Encode qw(encode);
use utf8;

my $word = Win32::OLE->new("Word.Application");
$word->{Visible} = -1;  # 可視状態にする (True = -1)

# documentは $word->Documents->Open で開く
# その時の引数をハッシュで準備する
my %openparam;  
$openparam{"FileName"} = Encode::encode("CP932","t:\\サンプル文書.docx");
$openparam{"ReadOnly"} = -1; # True 読み取り専用で開く

# ファイルを開く
my $doc = $word->Documents->Open(\%openparam);  # 引数のハッシュはリファレンスで渡す

# 差し込み印刷
my %dbparam;
$dbparam{"Name"} = encode("CP932","t:\\サンプル差込データ.xlsx");  # 差し込み印刷用のデータ
$dbparam{"SQLStatement"} = "SELECT * FROM `Sheet1\$`";  # シート名は〔`〕で囲む。また名称末尾には$

$doc->MailMerge->OpenDataSource(\%dbparam); # 差し込み印刷の設定 ハッシュをリファレンスで渡す    
$doc->MailMerge->{Destination} = 0; # wdSendToNewDocument = 0
$doc->MailMerge->{SuppressBlankLines} = -1; # True = -1
$doc->MailMerge->DataSource->{FirstRecord} = 1; # wdDefaultFirstRecord = 1 レコードの最初から
$doc->MailMerge->DataSource->{LastRecord} = -16; # wdDefaultLastRecord = -16 レコードの最後まで
$doc->MailMerge->Execute(0);    # 差し込み印刷実行

# 差し込み印刷された文書を印刷
my $countdoc = $word->Documents->Count; # 文書数、最後に作られた文書は 文書数-1で指定できる
my $newdoc = $word->Documents->Item($countdoc - 1);

my %printparam;
$printparam{"Copies"} = 1;  # コピー枚数
$printparam{"Background"} = 0;  # False = 0
$newdoc->PrintOut(\%printparam);

# 保存
$newdoc->SaveAs2(encode("CP932","t:\\サンプル保存.docx"));

# 閉じる
$newdoc->Close(0); # False = 0
$doc->Close(0);
$word->Quit(0);

undef $doc;
undef $word;

おわりに

 マクロの記録を使えばVBAコードが作成されますので、「ブックマークに移動」「表を結合」「斜線を引く」等も簡単にperlに移行できたりします。
 Excelも同様にマクロを記録して出来たVBAを加工すると、少し楽が出来ます。


明日、13日目はomokawa_yasuです。
ありがとうございました。