特許技術者の憂鬱・明細書の段落番号付与



# ユースケース
或るブラック特許事務所の午後。

ワイ「あー、今日もワード、明日もワードで明細書書きや。コロナ禍いうても出社してるんや。」
ワイ「かといってテレワークやと画面の反応が遅くてストレスやしなあ。」
ブラック所長「今日が納品締切日の、Ａ社さんへの特許明細書できたか？」
ワイ「はい、できました！」

```:ワイ君の明細書原稿
【書類名】明細書
【発明の名称】物質エネルギ変換装置
【技術分野】
　【０００１】
　本発明は、物質をエネルギに変換する物質エネルギ変換装置である。
【背景技術】
　【０００２】
　従来、エネルギを得るには、化学反応または核反応を用いる必要があり、効率が悪かっ
た。
　【０００３】
　非特許文献１には、反物質が物質と合わさって厖大なエネルギを発することが記載され
ている。
【先行技術文献】
　【０００４】
　【非特許文献】
　【０００５】
　　【非特許文献１】ダン・ブラウン、"天使と悪魔”、２０００年初版
【発明の概要】
【発明が解決しようとする課題】
　【０００６】
　非特許文献１では、反物質を生成する必要があった。反物質の生成には、この反物質が
物質と合わさってエネルギを生成する以上のエネルギが必要であった。
　【０００６】
　そこで、本発明は、物質をそのままエネルギに変換することを課題とする。
【課題を解決するための手段】
　【０００７】
　本発明の物質エネルギ変換装置は、水素を入力する水素入力手段と、当該水素を吸収す
る希土類金属結晶部と、当該希土類金属結晶部の所定の結晶方向に磁場を与える磁場形成
部とを有する。
【発明の効果】
　【０００８】
　本発明によれば、高い効率で物質をエネルギーに変換することができる。
【図面の簡単な説明】
　【０００９】
　【図１】本実施形態の物質エネルギ変換装置の構成図である。
　【図２】物質エネルギ変換装置の動作を説明する図である。
【発明を実施するための形態】
　【００１０】
　図１は、本実施形態の物質エネルギ変換装置１の構成図である。
　【００１０】
　物質エネルギ変換装置１は、水素入力部１１と、希土類金属結晶１２と、磁場形成部１
３と、これらを包み込む中性子遮蔽部１４とを備える。この物質エネルギ変換装置１は、
水素原子を直接にエネルギに変換するものである。
　【００１１】
　水素入力部１１は、・・・・・・・・・・・・・・・・・・・・・・・・である。
　【００１２】
　希土類金属結晶１２は、・・・・・・・・・・・・・・・・・・・・・・・・であり、水
素入力部１１から入力された水素を吸収して・・・・・・・・・・・・・・・・・・・・・
・・・する。この希土類金属結晶１２は、・・・・・・・・・・・・・・・・・・・・・・
・することにより、・・・・・・・・・する。
　【００１３】
　磁場形成部１３は、・・・・・・・・・・・・・・・・・であり、希土類金属結晶の１１
１方向の磁場を形成して、・・・・・・・・・、・・・・・・・・・・・・・・・・・・・
する。これにより、希土類金属結晶１２に吸収されている水素原子は、・・・・・・・・・
・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・する。
　【００１４】
　中性子遮蔽部１４は、水素原子のエネルギ化に伴い、希土類金属結晶１２から出射される
中性子線を遮蔽する。この中性子遮蔽部１４は、例えば・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
・・・であり、・・・・・・・・・する。
　図２は、物質エネルギ変換装置の動作を説明する図である。
　【００１５】
　水素は、水素入力部１１によって高圧（例えば、１０万メガパスカル）に圧縮されて、希
土類金属結晶１２が位置する炉内に導かれる。希土類金属結晶１２の水素吸収能力により、
水素は、希土類金属結晶１２に吸収される。そして、磁場形成部１３は、・・・・・・・・
・・・・・・・・・・・・・。これにより、希土類金属結晶１２からエネルギが放出され、
中性子線が出射される。
　【００１６】
　物質エネルギ変換装置１が変換するエネルギEは、以下の式（１）によって示される。
但し、Mは水素の質量である。Cは光速である。
　【数１】
    E=MC^2・・・（１）
　よって、本実施形態の物質エネルギ変換装置１によれば、反物質を生成することなく、
物質を直接的にエネルギに変換することができる。
【符号の説明】
　【００１７】
１　物質エネルギ変換装置
１１　水素入力部
１２　希土類金属結晶
１３　磁場形成部
１４　中性子遮蔽部
```

ブラック所長「なんやこれは、段落番号がデタラメやないか！」
ブラック所長「なんで【先行技術文献】の直後に段落番号【０００４】を入れてるんや！　段落番号を入れてもええ項目と、段落番号を入れてはいかん項目を覚えや。」
ブラック所長「ほんで、数式【数１】と文章との区切りには段落番号を入れんとな。」
ブラック所長「【００１０】の次にまた【００１０】があるぞ、連番で記載な！」
ブラック所長「図１の説明のあたりは段落番号を入れすぎ！　改行があるからいうて、１行ごとに機械的に入れたらあかん。クライアント企業から水増しやゆうてクレームくるぞ。」
ブラック所長「ここ、『図２は、物質エネルギ変換装置の動作を説明する図である。』ゆうて、違う図面のことを書き始めとるから、こういうところに段落番号を入れんとあかんのやが、なんでここに入っとらんのや。この次に段落番号【００１５】が入っとるけど、ここは意味としての区切りやないやろ。」

ワイ「は、はい（白目）」
ブラック所長「段落番号の振り方がええ加減やと、クライアント企業に、中身もその程度の品質だって思われるぞ！」
ブラック所長「納品時に厳しくチェックされるし注意せなあかん！」
ワイ「はい（白目）」
ブラック所長「ほな、ワシはとりあえず帰るぞ。」
・・・
ワイ「あー、ブラック所長はとっとと帰ってしもうたか。なんや最近の所長は、どうでもいい重箱の隅つつきばっかやなあ。まあええわ、こんな重箱の隅なところは、プログラムで生成したるぞ。」

ワイ「段落番号を連番にするだけやったら、WORDのフィールドコードでもええんやがな。段落番号を入れてはいけない項目だとか、段落番号を入れなきゃいけない項目だとか、覚えるのも面倒だし。意味の区切りとかもあるしな。」

# 特許明細書の書式調査
ワイ　「特許明細書の書式について書かれた文献を見つけたで。根拠条文は、特許法施行規則第２４条の様式第29やな。」

https://www.jpo.go.jp/system/laws/rule/guideline/document/syutugan_tetuzuki/02_03.pdf

>「18 明細書（配列表は除く。）には、原則として、発明の詳細な説明の段落、図面の簡単な説明の図の説明若しくは符号の説明又は配列表フリーテキストの繰り返し記載の前に、それぞれ「【」及び「】」を付した４桁のアラビア数字で「【０００１】」、「【０００２】」のように連続した段落番号を付す。

ワイ　「つまり、段落番号は４桁の連番ということやな。」

>この場合において、「【技術分野】」、「【背景技術】」、「【特許文献】」、「【非特許文献】」、「【発明の概要】」、「【発明が解決しようとする課題】」、「【課題を解決するための手段】」、「【発明の効果】」、「【図面の簡単な説明】」、「【発明を実施するための形態】」、「【実施例】」、「【産業上の利用可能性】」、「【符号の説明】」、「【受託番号】」又は「【配列表フリーテキスト】」の見出しの次に段落番号を付し、これらの見出しの前に段落番号を付してはならない。

ワイ　「【先行技術文献】は、ここに挙げた中にないのに段落番号を付けたからあかんかったのな。」

>また、「【特許文献１】」、「【非特許文献１】」、「【化１】」、「【数１】」、「【表１】」、「【図１】」のような番号の次に段落番号を付してはならない。」

ワイ　「他に段落番号を付けたらあかんのは、「【書類名】」、「【発明の名称】」、「【発明の概要】」やな。」


# 従来技術調査
ワイ　「vectorとかで段落番号の自動付与や振り直し機能を持つものは色々あるなあ、DANRAKU, 特許明細書の段落番号自動置換マクロ, 特許出願支援プログラム 段落振り版, 明細書再付番マクロ・・・」
ワイ　「ユーザインタフェースは参考になるな、段落番号を自動付与する機能、段落番号を連番に振り直す機能、段落番号を削除する機能があればええんやな。」
ワイ　「でも、どのソフトウエアも、ブラック所長の好みにあいそうな段落番号の振り方じゃないなあ・・・。」

|名称 | 概要 | 作者 |
|:---------------------------|:------------------|:-------------------|
| [DANRAKU](https://www.vector.co.jp/soft/winnt/business/se508771.html?_ga=2.208341205.656111451.1593267346-1579654811.1591502522)            |【段落番号】の自動付与・振り直しなどを1クリックで行うためのアドイン 英文明細書の形式にも対応| Clockahead||
| [特許明細書の段落番号自動置換マクロ(Word用)](https://www.vector.co.jp/soft/win95/business/se299105.html?_ga=2.53095663.656111451.1593267346-1579654811.1591502522) |明細書中の段落番号を自動的に振り直す(新旧様式対応) |岡田稔|
| [特許出願支援プログラム 段落振り版](https://www.vector.co.jp/soft/win95/business/se484499.html?_ga=2.246539459.656111451.1593267346-1579654811.1591502522) |特許明細書のWordファイルに対して新規段落振り・段落振り直しを行う|ソフト研究会（ライフラボ株式会社）|
| [特許明細書の段落番号等を操作するマクロ](https://www.vector.co.jp/soft/winnt/business/se434178.html) |番号付与や再付与をするマクロ 【化】【数】【表】や【化1-1】等、HTMLファイルにも再付与が可能|えの|
| [明細書再付番マクロ(MS-Word)](https://www.vector.co.jp/soft/win95/business/se377703.html?_ga=2.231894815.1469615736.1593342817-1960651967.1586016428) |特許明細書の各種項番を振り直す|(有)DJソフト|

## 仕様
ワイ　「Visual Studio 2019でWORD VSTO (Visual Studio Tools for Office)アドインとして開発しよ。」
同僚Ｘ「おお、ワイ君はなんかまた作り始めたなあ。」
ワイ　「ええ、段落番号の入力を自動化するんですよ。」
同僚Ｘ「ちゃんとできたら皆にも公開な。」
ワイ　「は、はい。・・・（公開を考えて、VBAじゃなくVSTOで作成して良かった・・・）」

## セットアップ
ワイ　「はい、エクスさん使ってみてください。」
同僚Ｘ「おお、ちゃんとセットアップもあるなあ。」
ワイ　「・・・（Visual Studioの発行メニューをクリックしただけやけどな。）」
同僚Ｘ「なんやエラーが出てインストールできないぞ。」
![01SmartScreen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/8bdfce28-7962-cb75-fac7-8d1c65157a2e.png)
ワイ　「これは、詳細情報をクリックしてください。」

![02SmartScreen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/8b70541e-a123-a299-36d7-6e48978465b4.png)
ワイ　「この実行ボタンをクリックします。」
![03vstoerror.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/9eca4336-54f0-ab67-6c11-79ab1d13df08.png)

同僚Ｘ「なんやエラーが出てインストールできないぞ。」
ワイ　「おかしいなあ」
・・・
ワイ　「わかりましたよ。インターネットからダウンロードしたファイルには、『ゾーン識別子』が付いてるんですけど、このせいです。対策しますから待ってください。」


VSTOを用いて作成したアドインの配布方法
https://wiki.clockahead.com/index.php?Coding%2FMSOffice%2FVSTO%2F%C7%DB%C9%DB%A4%CE%CA%FD%CB%A1

VSTOにおいてSecurityExceptionによってファイルがインストールできないとき
http://artfulplace.hatenablog.com/entry/2014/05/26/154251

## セットアップ再び
ワイ　「はい、エクスさん使ってみてください。」
同僚Ｘ「setup.batをクリックするんやな。」

![03vsto.install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/96405924-f08a-3b54-2b1f-da6ab4f4fdaf.png)

同僚Ｘ「セットアップは無事終わり。そしたら、WordにAppLintとかいうリボンができるんだな。」

ワイ　「これが段落追加ボタンです。ボタンを押すと段落が自動で付きますよ。」
![add1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/75115f8e-2677-0bac-97be-9cc350fb32ad.png)

ワイ　「これが段落振り直しボタンです。ボタンを押すと、既存の段落を追番に振り直します。」
![renum1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/3457f777-212f-a98b-26d8-4707c88d4d2a.png)

ワイ　「これが段落削除ボタンです。最初はまっさらにした方がいいときもあります。」
![del1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/703ef280-198d-4daa-2ddc-347ba429ca86.png)


同僚Ｘ「クライアントさんから帰ってきたチェック済原稿を開いて、段落番号追加ボタンをクリック、と。」
<img width="616" alt="変更履歴on.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/0f00bc1d-9424-84a6-45d4-a04d27931a74.png">


同僚Ｘ「おい、校閲の変更履歴が記録されまくって、グダグダになってしまっているぞ。」
ワイ　「ええっ、エクスさん、ワード文書の変更履歴の記録はオフにしていただかないと。」
同僚Ｘ「なんとかならんかなあ、他人から回されてきたワード原稿とかだと、変更履歴の記録がオンされているか否かなんて、
　　　　ワードの校閲リボンから、「変更履歴の記録」をクリックせんとわからんしなあ。」
ワイ　「そ、そうですねえ。」
同僚Ｘ「それに、これっていつ始まって、いつ終わったかもわからんなあ。中断もできんし。」
ワイ　「短時間やからええと思ってましたが、気になるもんですねえ。」
同僚Ｘ「ほら、画面上に横棒が表示されて、それが１００％になったときに終わるやつをつけてや。
　　　　途中で止めるためのボタンもつけてな。」
ワイ　「は、はい。・・・（ブログレスバーとキャンセルボタンのことだな、）」


同僚Ｘ「僕の持っている明細書原稿を全部掛けてみようか。」
　　　　・・・
同僚Ｘ「ワイ君、なんか、この明細書原稿、ぜんぜん段落番号の削除が効かないけど。」
ワイ　「あ、あれっ変ですねえ。」
<img width="614" alt="改行.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/a0061f3b-90d8-fb55-b66e-0322cc46b3bd.png">

ワイ　「この明細書原稿の改行マーク、なんかおかしくないですか。」

　　　　・・・・

ワイ　「エクスさん、わかりましたよ。この明細書原稿って、Enterじゃなくて、Shift+Enterで行の区切りが入力されているんですよ。」
同僚Ｘ「ふーん、なんか知らんけど、どういう風に入力してもちゃんと段落番号を付けるようにしてな。」
ワイ　「・・・（Enterで入力されるのが ^pで、Shift+Enterで入力されるのが ^l か、全部 ^pに置換するか・・・）」

## 改良版
ワイ　「はい、エクスさん、改良しましたので使ってみてください。」
同僚Ｘ「このあいだのクライアントさんのチェック済原稿を掛けてみよう。
　　　　ちゃんと、「校閲の変更履歴の記録をオフしてください。」で出るなあ。」
ワイ　「はい、校閲の変更履歴の記録かオンのときには、処理せずに警告するようにしたんですよ。」
同僚Ｘ「校閲の変更記録をオフして、段落番号振直ボタンをクリック、と。
　　　　進捗を示す棒グラフと、キャンセルボタンも表示されたなあ。」
ワイ　「はい」

同僚Ｘ「ぜんぜん段落番号を振ってくれなかった明細書原稿もチェック、と。
　　　　おお、こんどはちゃんと段落番号を付けてくれるなあ。」
ワイ　「はい。対応しました。
　　　　（こっそりと ^l を ^p に書き換えるのでＯＫだったか。）

ブラック所長「おお、ワイ君やないか。ほう、段落番号を入れてくれるWORDアドインか。」
ワイ　「は、はい。」
ブラック所長「なかなかええ感じに振られてるなあ。
　　　　　　　このあいだ苦言を呈させてもろた原稿を掛けてみよっか。」
ワイ　「はい」

```:ワイ君の明細書原稿
【書類名】明細書
【発明の名称】物質エネルギ変換装置
【技術分野】
　【０００１】
　本発明は、物質をエネルギに変換する物質エネルギ変換装置である。
【背景技術】
　【０００２】
　従来、エネルギを得るには、化学反応または核反応を用いる必要があり、効率が悪かっ
た。
　非特許文献１には、反物質が物質と合わさって厖大なエネルギを発することが記載され
ている。
【先行技術文献】
　【非特許文献】
　【０００３】
　　【非特許文献１】ダン・ブラウン、"天使と悪魔”、２０００年初版
【発明の概要】
【発明が解決しようとする課題】
　【０００４】
　非特許文献１では、反物質を生成する必要があった。反物質の生成には、この反物質が
物質と合わさってエネルギを生成する以上のエネルギが必要であった。
　そこで、本発明は、物質をそのままエネルギに変換することを課題とする。
【課題を解決するための手段】
　【０００５】
　本発明の物質エネルギ変換装置は、水素を入力する水素入力手段と、当該水素を吸収す
る希土類金属結晶部と、当該希土類金属結晶部の所定の結晶方向に磁場を与える磁場形成
部とを有する。
【発明の効果】
　【０００６】
　本発明によれば、高い効率で物質をエネルギーに変換することができる。
【図面の簡単な説明】
　【０００７】
　【図１】本実施形態の物質エネルギ変換装置の構成図である。
　【図２】物質エネルギ変換装置の動作を説明する図である。
【発明を実施するための形態】
　【０００８】
　図１は、本実施形態の物質エネルギ変換装置１の構成図である。
　物質エネルギ変換装置１は、水素入力部１１と、希土類金属結晶１２と、磁場形成部１
３と、これらを包み込む中性子遮蔽部１４とを備える。この物質エネルギ変換装置１は、
水素原子を直接にエネルギに変換するものである。
　【０００９】
　水素入力部１１は、・・・・・・・・・・・・・・・・・・・・・・・・である。
　希土類金属結晶１２は、・・・・・・・・・・・・・・・・・・・・・・・・であり、水
素入力部１１から入力された水素を吸収して・・・・・・・・・・・・・・・・・・・・・
・・・する。この希土類金属結晶１２は、・・・・・・・・・・・・・・・・・・・・・・
・することにより、・・・・・・・・・する。
　【００１０】
　磁場形成部１３は、・・・・・・・・・・・・・・・・・であり、希土類金属結晶の１１
１方向の磁場を形成して、・・・・・・・・・、・・・・・・・・・・・・・・・・・・・
する。これにより、希土類金属結晶１２に吸収されている水素原子は、・・・・・・・・・
・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・する。
　【００１１】
　中性子遮蔽部１４は、水素原子のエネルギ化に伴い、希土類金属結晶１２から出射される
中性子線を遮蔽する。この中性子遮蔽部１４は、例えば・・・・・・・・・・・・・・・・
・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・・
・・・であり、・・・・・・・・・する。
　【００１２】
　図２は、物質エネルギ変換装置の動作を説明する図である。
　水素は、水素入力部１１によって高圧（例えば、１０万メガパスカル）に圧縮されて、希
土類金属結晶１２が位置する炉内に導かれる。希土類金属結晶１２の水素吸収能力により、
水素は、希土類金属結晶１２に吸収される。そして、磁場形成部１３は、・・・・・・・・
・・・・・・・・・・・・・。これにより、希土類金属結晶１２からエネルギが放出され、
中性子線が出射される。
　【００１３】
　物質エネルギ変換装置１が変換するエネルギEは、以下の式（１）によって示される。
但し、Mは水素の質量である。Cは光速である。
　【数１】
    E=MC^2・・・（１）

　【００１４】
　よって、本実施形態の物質エネルギ変換装置１によれば、反物質を生成することなく、
物質を直接的にエネルギに変換することができる。
【符号の説明】
　【００１５】
１　物質エネルギ変換装置
１１　水素入力部
１２　希土類金属結晶
１３　磁場形成部
１４　中性子遮蔽部
```
ブラック所長「「【先行技術文献】」の次には段落番号を入れてないな。」
ワイ　「段落番号を入れていい項目と入れてはならない項目は、ちゃんと区別してます。」


ブラック所長「程よい感じで段落番号が入ってるな。」
ワイ　　「はい。１行４０文字で書いたとき、およそ３～５行で１つの段落番号が入るようにしたんです。」

ブラック所長「【数１】と数式の次にも入ってるな。」
ワイ　「ワードの数式・画像・表をちゃんと判定してます。」
　　　　（・・・テキストで数式を書かれたらアウトやけどな。）

ブラック所長「実施形態の『図１は、・・。』、『図２は、・・。』の直前に段落番号が入ってるな。」
ワイ　「はい、『図（番号）は、・・・。』を検知して、その直前に段落番号を入れるようにしてます。」

ブラック所長「これなら段落番号は大丈夫や。さて、次は中身の問題やが・・・。」
ワイ　ギクッ・・・　　

（次回に続く・・・かもしれない。）

※ワイ記法(C)無職やめ太郎さん

# 仕様説明
これは、フリーソフト「段落ジェネレータ」の紹介記事です。

GitHub のURLはこちらです。Visual Studio 2019 でコンパイルしてください。
https://github.com/k-ayaki/danrakuG01

OSDN のURLはこちらです。インストーラがアップロードされています。
https://osdn.net/projects/danrakug/

# コードとプロジェクトの説明
WORD のVSTOアドインについて書かれている文献は少ないので、この記事が少しでも開発の助けになればとおもいます。試しに、amazon で "VSTO" と入力して書籍を検索してみてください。"VBA"の書籍と比べて、その少なさに愕然とします。

　なお、VSTO(Visual Studio Tools for Office)とは、オフィスソリューションの一つであり、OfficeアプリケーションのアドインをC#やVB.netで記述できます。VBA(Visual Basic for Application)と比べると、Visual Studioが必須である点が、やや面倒くさいです。また、VSTOのアドインは、Officeのバージョンによっては互換性が問題になる場合があります。しかし、Officeのアドインを作成して公開し、かつ、そのソースコードを秘匿したい場合には、VSTOを選択肢とすることが必要とおもいます。

## 参考文献
オフィスソリューション開発の概要 (VSTO)
https://docs.microsoft.com/ja-jp/visualstudio/vsto/office-solutions-development-overview-vsto?view=vs-2019

## GUIの説明
GUIは、WORDのリボンインタフェースとして実装いたしました。

リボン全体の属性は以下です。
<img width="764" alt="2020-06-28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/c783354e-b6dd-04c4-d503-f44b284a0c83.png">

ボタンのグループの名前には「段落生成」と名付けました。
<img width="767" alt="2020-06-28 (4).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/a33a3793-b704-d488-d3f2-db8b4c35de7e.png">

　段落付与ボタンは、縦横64ピクセルのアイコンを付けました。“【０００１】”の段落番号を黒文字で入れています。なお、自前でアイコンを生成する代わりに、Officeに組み込まれているアイコンを使うこともできます。
　この段落付与ボタンをクリックしたとき、Ribbon1.cs の AddDanraku_Click関数が呼び出されます。
　これにより、段落が付与されたのち、段落番号が追番に振り直されます。

<img width="767" alt="2020-06-28 (1).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/32ef1f4e-d93a-af5c-6721-5fa75301c5ed.png">

　段落の振直ボタンは、縦横64ピクセルのアイコンを付けました。“【０００１】”と”【０００２】”の段落番号がオーバーラップするよう青文字で入れています。振直ボタンをクリックしたとき、Ribbon1.cs の  RenumDanraku_Click関数が呼び出されます。
　これにより、段落番号が追番に振り直されます。

<img width="767" alt="2020-06-28 (2).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/e2776c5a-085b-e8e6-c91a-1a9d859eadaa.png">

　段落の削除ボタンは、縦横64ピクセルのアイコンを付けました。“【０００１】”と取消線とを赤で入れています。削除ボタンをクリックしたとき、Ribbon1.cs の  DelDanraku_Click関数が呼び出されます。
　これにより、段落が削除されます。

<img width="767" alt="2020-06-28 (3).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266273/5268ae5c-bb97-ebcf-bb01-e8912d0469f1.png">
　
# コード説明
## 段落の削除
　一番簡単な段落削除から説明します。

　最初に、WORDの校閲の変更履歴の記録がオンになっているか否かを判定します。変更履歴の記録がオンになっているときにアドインが文書を書き換えると、その書き換えが変更履歴として記録されてしまいます。よっで、これを防ぐために、変更履歴の記録がオンになっているときには、処理を実行せずに終了します。
　doc.TrackRevisionsがtrue のとき、変更履歴の記録がオンです。このときには、メッセージボックスを表示したのち、処理を終了します。

　次に、文書の垂直タブを改行に変換します。WORDのパラグラフは、文章の改行で区切られています。
　WORDの文章が垂直タブで区切られていると、そこは文章の切れ目であり、かつ、文章が改行されているかのように見えますが、垂直タブの前後は同一のパラグラフです。
　よって、本アドインでは文章の垂直タブを改行に変換して、文章の切れ目とパラグラフの区切りを一致させています。

　そして、プログレスパーの表示用ダイアログと、プログレスバーのワーカースレッドを起動します。なお ProgressDialog クラスは、プログレスバーの表示用ダイアログが終了すると、その結果に応じて、キャンセル、エラー、成功の何れかに分岐します。

```csharp:DelDanraku_Click（段落削除ボタンのクリック）
        private void DelDanraku_Click(object sender, RibbonControlEventArgs e)
        {
            Document doc = danrakuG01.Globals.ThisAddIn.Application.ActiveDocument;
            var myPatDoc = new patDoc();

            if (doc.TrackRevisions == true)
            {
                System.Windows.Forms.MessageBox.Show("変更履歴の記録をオフしてくたさい");
                return;
            }
            myPatDoc.垂直タブを改行に(doc);
            ProgressDialog pd = new ProgressDialog("段落の削除",
                new DoWorkEventHandler(ProgressDialog_Del_DoWork),
                16);
            //進行状況ダイアログを表示する
            DialogResult result = pd.ShowDialog();
            //結果を取得する
            if (result == DialogResult.Cancel)
            {
                MessageBox.Show("キャンセルされました");
            }
            else if (result == DialogResult.Abort)
            {
                //エラー情報を取得する
                Exception ex = pd.Error;
                MessageBox.Show("エラー: " + ex.Message);
            }
            else if (result == DialogResult.OK)
            {
                //結果を取得する
                int stopTime = (int)pd.Result;
                //MessageBox.Show("成功しました: " + stopTime.ToString());
            }
            //後始末
            pd.Dispose();
        }
```
## 垂直タブを改行に
　垂直タブを改行に変換するコードは、マイクロソフトのVSTOのサンプルコードとほぼ同一です。

　プログラムによって文書内のテキストを検索および置換する
https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-programmatically-search-for-and-replace-text-in-documents?view=vs-2019

  プログラムによって検索後に選択を復元する
https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-programmatically-restore-selections-after-searches?view=vs-2019

```csharp:垂直タブを改行に
        // プログラムによって文書内のテキストを検索および置換する
        // https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-programmatically-search-for-and-replace-text-in-documents?view=vs-2019
        // プログラムによって検索後に選択を復元する
        //https://docs.microsoft.com/ja-jp/visualstudio/vsto/how-to-programmatically-restore-selections-after-searches?view=vs-2019
        public void 垂直タブを改行に(Document doc)
        {
            object missing = null;

            Range rangeSave = doc.Application.Selection.Range;
            doc.Application.Selection.WholeStory();
            Find findObject = doc.Application.Selection.Find;
            findObject.ClearFormatting();
            findObject.Text = "^l";
            findObject.Replacement.ClearFormatting();
            findObject.Replacement.Text = "^p";
            findObject.MatchFuzzy = false;
            findObject.Forward = true;
            object findtext = "^l";
            object replacetext = "^p";
            object replaceAll = WdReplace.wdReplaceAll;
            findObject.Execute(ref findtext, ref missing, ref missing, ref missing, ref missing,
                ref missing, ref missing, ref missing, ref missing, ref replacetext,
                ref replaceAll, ref missing, ref missing, ref missing, ref missing);
            rangeSave.Select();
         }
```
　段落削除に係るプログレスバーのワーカースレッドです。
　このスレッド内では、WORDの文書のうち、書類名の範囲が“明細書”であるものを検索します。
　すなわち、“【書類名】明細書”が記載されたパラグラフから、次の“【書類名】”が記載されたパラグラフまたは文書の末尾までの範囲を取得します。
　そして、段落番号が記載されているパラグラフを順次検索すると、そのパラグラフを削除します。

```csharp:ProgressDialog_Del_DoWork（段落削除のプログレスバーのワーカースレッド）
        // 段落の削除
        private void ProgressDialog_Del_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker bw = (BackgroundWorker)sender;

            //パラメータを取得する
            int stopTime = (int)e.Argument;

            Document doc = danrakuG01.Globals.ThisAddIn.Application.ActiveDocument;
            var myPatDoc = new patDoc();
            Range rng = myPatDoc.書類名の範囲("明細書", doc);
            if (rng == null)
            {
                System.Windows.Forms.MessageBox.Show("明細書が記載されていません。", "警告");
                e.Result = 0;
                return;
            }
            long endpos = rng.End;
            int counter = 0;
            int i = 0;
            rng.Find.MatchWildcards = true;

            int lastTick = Environment.TickCount;
            int currTick;
            bw.ReportProgress(i, i.ToString() + "% 終了しました");

            while (rng.Find.Execute("【[０-９]@】"))
            {
                counter++;

                rng.Text = "";
                rng.SetRange(rng.End, rng.End);
                if (myPatDoc.パラグラフが空白か判定(rng.Paragraphs[1]))
                {
                    rng.Paragraphs[1].Range.Delete();
                }
                //キャンセルされたか調べる
                if (bw.CancellationPending)
                {
                    //キャンセルされたとき
                    e.Cancel = true;
                    return;
                }
                currTick = Environment.TickCount;
                if( currTick - lastTick > 1000 )
                {
                    //指定された時間待機する
                    //System.Threading.Thread.Sleep(stopTime);

                    //ProgressChangedイベントハンドラを呼び出し、
                    //コントロールの表示を変更する
                    i = (int)(rng.End * 100 / endpos);
                    bw.ReportProgress(i, i.ToString() + "% 終了しました");
                    lastTick = currTick;
                }
            }
            i = 100;
            bw.ReportProgress(i, i.ToString() + "% 終了しました");
            System.Threading.Thread.Sleep(500);
            //結果を設定する
            e.Result = counter;
        }
```

　プログレスダイアログは、ProgressDialog.cs に記載された ProgressDialogクラスによって表示されます。このクラスは、BackgroundWorkerクラスを使用しており、以下のサンプルコードを参考にしています。

参考文献：
BackgroundWorkerクラスを使用して進行状況ダイアログを作成する
https://dobon.net/vb/dotnet/programing/progressdialogbw.html

　“書類名の範囲”メソッドは、docname で記載された名前に一致する書類名の範囲を返すものです。
　こういう風に、メソッド名を日本語で書くと嫌がる方がおられますが、自分としては日本人ならば可読性が上がるからよいとおもって書いています。

```csharp:書類名の範囲
        public Range 書類名の範囲(
            string docname,
            Document doc,
            long spos = 0,
            long epos = -1
            )
        {
            Range rng;
            if (epos == -1)
            {
                epos = doc.Content.End;
            }
            rng = null;
            while (書類名の範囲を選択(doc, ref rng, spos, epos) == true)
            {
                if (rng == null)
                {
                    break;
                }
                string para1 = rng.Paragraphs[1].Range.Text;

                if (para1.IndexOf(docname) > 0)
                {
                    領域始端のトリム(ref rng);
                    領域終端のトリム(ref rng);
                    領域拡張(ref rng);
                    break;
                }
                spos = rng.End;
            }
            return rng;
        }
```

　書類名の範囲を選択メソッドは、“【書類名】・・・・” から次の “【書類名】・・・・” までを選択するものです。
　領域始端のトリムメソッドとは、始端から空白・改行などを取り去るものです。これにより領域の始端を調整します。
　領域終端のトリムメソッドとは、終端から空白・改行などを取り去るものです。これにより領域の終端を調整します。
　領域拡張メソッドは、トリムした始端・終端を、WORD文書のパラグラフ単位で拡張します。


## 段落の生成
  段落の生成を説明します。

　最初に、WORDの校閲の変更履歴の記録がオンになっているか否かを判定します。変更履歴の記録がオンになっているときにアドインが文書を書き換えると、その書き換えが変更履歴として記録されてしまいます。よっで、これを防ぐために、変更履歴の記録がオンになっているときには、処理を実行せずに終了します。
　doc.TrackRevisionsがtrue のとき、変更履歴の記録がオンです。このときには、メッセージボックスを表示したのち、処理を終了します。
　次に、文書の垂直タブを改行に変換します。

　そして、段落生成のプログレスパーの表示用ダイアログと、段落生成のプログレスバーのワーカースレッドを起動します。
　プログレスバーの表示用ダイアログが終了すると、その結果に応じて、キャンセル、エラー、成功の何れかに分岐します。

　次に、段落番号の振り直しのプログレスパーの表示用ダイアログと、段落番号の振り直しのプログレスバーのワーカースレッドを起動します。
　これは、既に段落番号が付与されているとき、新たに段落番号を追加すると、段落番号の振り直しが必要になるためです。
　プログレスバーの表示用ダイアログが終了すると、その結果に応じて、キャンセル、エラー、成功の何れかに分岐します。

```csharp:AddDanraku_Click（段落生成ボタンのクリック）
        private void AddDanraku_Click(object sender, RibbonControlEventArgs e)
        {
            Document doc = danrakuG01.Globals.ThisAddIn.Application.ActiveDocument;

            var myPatDoc = new patDoc();
            //myPatDoc.G_段落番号付与(doc);
            if (doc.TrackRevisions == true)
            {
                System.Windows.Forms.MessageBox.Show("変更履歴の記録をオフしてくたさい");
                return;
            }
            myPatDoc.垂直タブを改行に(doc);
            //ProgressDialogオブジェクトを作成する
            ProgressDialog pd = new ProgressDialog("段落番号の付与",
                new DoWorkEventHandler(ProgressDialog_Add_DoWork),
                16);
            //進行状況ダイアログを表示する
            DialogResult result = pd.ShowDialog();
            //結果を取得する
            if (result == DialogResult.Cancel)
            {
                MessageBox.Show("キャンセルされました");
                //後始末
                pd.Dispose();
                return;
            }
            else if (result == DialogResult.Abort)
            {
                //エラー情報を取得する
                Exception ex = pd.Error;
                MessageBox.Show("エラー: " + ex.Message);
                //後始末
                pd.Dispose();
                return;
            }
            else if (result == DialogResult.OK)
            {
                //結果を取得する
                int stopTime = (int)pd.Result;
                //MessageBox.Show("成功しました: " + stopTime.ToString());
            }
            //後始末
            pd.Dispose();

            //ProgressDialogオブジェクトを作成する
            pd = new ProgressDialog("段落番号の振り直し",
                new DoWorkEventHandler(ProgressDialog_Renum_DoWork),
                16);
            //進行状況ダイアログを表示する
            result = pd.ShowDialog();
            //結果を取得する
            if (result == DialogResult.Cancel)
            {
                MessageBox.Show("キャンセルされました");
            }
            else if (result == DialogResult.Abort)
            {
                //エラー情報を取得する
                Exception ex = pd.Error;
                MessageBox.Show("エラー: " + ex.Message);
            }
            else if (result == DialogResult.OK)
            {
                //結果を取得する
                int stopTime = (int)pd.Result;
                //MessageBox.Show("成功しました: " + stopTime.ToString());
            }
            //後始末
            pd.Dispose();
        }
```


　段落生成に係るプログレスバーのワーカースレッドでは、WORDの文書のうち、書類名の範囲が“明細書”であるものを検索します。すなわち、“【書類名】明細書”が記載されたパラグラフから、次の“【書類名】”が記載されたパラグラフの直前のパラグラフまたは文書の末尾までの範囲を取得します。
　そして、範囲内のパラグラフを順次検索すると、そのパラグラフの直後または直前が段落番号の生成対象であるか否かを判定します。段落番号の生成対象であると判定したならば、そのパラグラフの直後または直前に、段落番号が記載されたパラグラフを挿入します。

　“項目の判定”メソッドとは、現在のパラグラフに隅付括弧【】があるか否かを判定するものです。
　そして、“パラグラフが段落番号付与対象項目か判定”メソッドがtrueならば、この直後に段落番号のパラグラフを挿入します。そして、現在のパラグラフが”【符号の説明】”ならば、処理を終了します。
　
 次に、”パラグラフが数化表項目か判定”メソッドにより、現在のパラグラフが数式、化学式、表の項目であるか否かを判定します。現在のパラグラフが数式、化学式、表の項目ならば、直前に文章が記載されているか否かを判定し、その直前に段落番号が記載されたパラグラフを挿入します。

　次に、”パラグラフが段落番号付与非対象項目か判定”メソッドがtrueであり、かつ“パラグラフが不正な段落番号か判定”メソッドがtrueならば、不正な段落番号が付与されたパラグラフとして、このパラグラフを削除します。

```csharp:ProgressDialog_Add_DoWork（段落生成のプログレスバーのワーカースレッドの要部）
            int counter = 0;
            int i = 0;
            int lastTick = Environment.TickCount;
            int currTick;
            bw.ReportProgress(i, i.ToString() + "% 終了しました");

            foreach (Paragraph paraCurr in rng.Paragraphs)
            {
                string tmpdebug = paraCurr.Range.Text;
                if (myPatDoc.項目の判定(paraCurr))
                {
                    if (myPatDoc.パラグラフが段落番号付与対象項目か判定(paraCurr))
                    {
                        Range rng2 = myPatDoc.直後への段落番号の挿入(paraCurr);
                        if (paraCurr.Range.Text.IndexOf("【符号の説明】") >= 0)
                        {
                            break;
                        }
                    }
                    else if (myPatDoc.パラグラフが数化表項目か判定(paraCurr))
                    {
                        Paragraph paraPrev = myPatDoc.テキスト記載パラグラフ取得(paraCurr, -1);
                        if (myPatDoc.パラグラフが数化表項目か判定(paraPrev))
                        {
                            myPatDoc.直前への段落番号挿入(paraCurr);
                        }
                        paraPrev = null;
                    }
                    else if (myPatDoc.パラグラフが段落番号付与非対象項目か判定(paraCurr))
                    {
                        if (myPatDoc.パラグラフが不正な段落番号か判定(paraCurr))
                        {
                            paraCurr.Range.Delete();
                        }
                    }
                    else
                    {
                        Range rng2 = myPatDoc.直後への段落番号の挿入(paraCurr);
                    }
                }
                else
                {
                    myPatDoc.段落番号付与判定(paraCurr);
                }
                counter++;
                //キャンセルされたか調べる
                if (bw.CancellationPending)
                {
                    //キャンセルされたとき
                    e.Cancel = true;
                    return;
                }
                //指定された時間待機する
                //System.Threading.Thread.Sleep(stopTime);

                currTick = Environment.TickCount;
                if (currTick - lastTick > 1000)
                {
                    //ProgressChangedイベントハンドラを呼び出し、
                    //コントロールの表示を変更する
                    i = (int)(paraCurr.Range.End * 100 / rng.End);
                    bw.ReportProgress(i, i.ToString() + "% 終了しました");
                    lastTick = currTick;
                }
            }
```

## 段落の振り直し
  段落の振り直しは、追番として記載されていない段落番号を、追番に振り直すものです。具体的には、WORDの検索機能で、段落番号のパターンに当て嵌まる文字列を順次検索して、これを追番の段落番号の文字列で書き換えています。

```csharp:ProgressDialog_Renum_DoWork（段落の振り直しのプログレスバーのワーカースレッド）
            rng.Find.MatchWildcards = true;
            while (rng.Find.Execute("【[０-９]@】"))
            {
                counter++;
                rng.Text = myPatDoc.段落番号文字列の生成(counter);
                rng.SetRange(rng.End, rng.End);

                //キャンセルされたか調べる
                if (bw.CancellationPending)
                {
                    //キャンセルされたとき
                    e.Cancel = true;
                    return;
                }
                //指定された時間待機する
                //System.Threading.Thread.Sleep(16);

                currTick = Environment.TickCount;
                if (currTick - lastTick > 1000)
                {
                    //ProgressChangedイベントハンドラを呼び出し、
                    //コントロールの表示を変更する
                    i = (int)(rng.End * 100 / endpos);
                    bw.ReportProgress(i, i.ToString() + "% 終了しました");
                    lastTick = currTick;
                }
            }
```


## インストーラ
　最近のVSTOで特筆すべきは、インストーラの作成の簡単さだとおもいます。VisualStudio2019のビルドメニューから「・・・の発行」をクリックするだけですから。
　なお、CDやUSBメモリからインストールする従来のインストーラに限られず、FTPサーバにアップロードすることも可能だそうですが、未検証です。

　出来上がったセットアップファイルをインターネットに置くと、ダウンロードの際にゾーン識別子が付きます。vstoのsetup.exeは、*.vstoと *.dll.manifest にゾーン識別子が着いているとエラー終了します。よって、setup.bat によりゾーン識別子を削除して、setup.exeを起動させます。

```powershell:setup.bat
echo .>danrakuG01.vsto:Zone.identifier
echo .>".\Application Files\danrakuG01_1_0_0_4\danrakuG01.dll.manifest:Zone.identifier"
echo .>".\Application Files\danrakuG01_1_0_0_4\danrakuG01.vsto:Zone.identifier"
setup.exe
```

参考文献：
チュートリアル: Word 用の最初の VSTO アドインの作成します。
https://docs.microsoft.com/ja-jp/visualstudio/vsto/walkthrough-creating-your-first-vsto-add-in-for-word?view=vs-2019
【ワレコの講座】VS2017でEXCEL VSTOアドイン作成 – リボン編（1/10）
https://www.wareko.jp/blog/creating-excel-vsto-addin-with-vs2017-ribbon-edition-1of10
【ワレコの講座】EXCEL VSTOアドインで選択セル情報取得（2/10）
https://www.wareko.jp/blog/acquire-selected-cell-information-with-excel-vsto-add-in-2of10
【ワレコの講座】EXCEL VSTOアドインを他のPCに配布する（3/10）
https://www.wareko.jp/blog/publish-excel-vsto-add-in-to-another-pc-3of10
ダウンロードしたファイルの「ブロック解除」をコマンドで
https://qiita.com/gentaro/items/3beb65a8f2f89089a042