Help us understand the problem. What is going on with this article?

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等の定数を調べることができます。
002.png
 図では、True = -1, False = 0, wdSendToNewDocument = 0 だと判ります。

引数について

 関数への引数はハッシュのリファレンスで渡します。
 下図の赤枠はWordのマクロ記録で作成されたVBAです。Tドライブのサンプル文書というファイルを開いたものです。
001.png
 この場合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です。
ありがとうございました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away