DoxygenによるMarkdown形式からのドキュメント作成

32 min読了の目安(約29100字TECH技術記事

📌 はじめに

Doxygenはプログラムのコメントに書かれた情報をもとにドキュメントを作成するツールです.Doxygenはプログラムコードを解析する機能に特化していますが,Markdown形式に対応していて,コメントの中に含めることができます.また,プログラムコードのコメントでなければいけないのかというとそうでもなく,Markdownファイルのみから文書を作成することができます.さらにMarkdown形式とDoxygenの特殊コマンドを同時に使えます.ただし,それによる弊害も多少あり,使いづらいところもあったりします.

以前に,「MkDocsによるドキュメント作成」という記事を書きました.MkDocsは手軽で機能も揃っているので小規模なドキュメントならこれで十分です.ではDoxygenによるドキュメント文書の作成はどんなところがあるのかというと,これは個人的な意見ですが,HTMLHELP(.chm)やPDFなど様々な形式に変換する機能が備わっていることです.最近になって,doxygenを使ってHTMLHELP形式の文書を作成しているのですが,ある程度見た目をカスタマイズしたりして,良いところ・悪いところがあったのでまとめてみました.なので,HTML および HTMLHELP 形式で作成するドキュメントに特化したものになっています.

📌 使い方

以下のサイトからダウンロードしてインストールします.

基本的には Doxywizard を使ってGUIで操作すると楽です.今回はGUIを使わずにコマンドラインを使います.
タイトルにもある通り,Markdownファイルからドキュメントを作成していきます.また,Visual Studio Code (以下VSCode)を使う方法も合わせて紹介します.

📌 設定ファイル

まずは次のようなフォルダ構成にします.

./workspace         ルートフォルダ
    /build          ビルド用ファイル
    /src            Markdownファイル
        /images     画像ファイル
    /extra          外部ファイル

次に,Doxygenの設定ファイルを build フォルダに作ります.慣例的に Doxyfile という名前にするようです.初期は次のような内容にします.

Doxyfile

DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = Doxygenによるドキュメント作成
OUTPUT_DIRECTORY = 
OUTPUT_LANGUAGE = Japanese-en
ALIASES = 
MARKDOWN_SUPPORT = YES
GENERATE_HTML = YES
HTML_OUTPUT = html
INPUT = ../src/index.md ../src
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.md

各変数の詳細は公式のドキュメントを参照してください.FILE_PATTERNSではMarkdownファイル(.md),入力ファイルとして src/index.md を指定しています.次に src フォルダに index.md を作成します.これがトップページになります.

📌 index.md

# Hello Doxygen!    {#mainpage}
このドキュメントはDoxygenで作られています.

📌 ビルド

これで最低限の設定は終わったのでビルドします.buildフォルダにおいて以下のコマンドを実行します.

doxygen Doxyfile

VSCodeのビルドタスクに設定すると便利です.

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "doxygen Doxyfile",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "presentation": {
                "reveal": "always",
                "clear": true,
                "panel": "shared"
            },
            "options": {
                "cwd": "${workspaceRoot}/build"
            }
        }
    ]
}

Run Build Task コマンドでタスクが実行され,ドキュメントが作成されます.

...
Generating alphabetical compound index...
Generating hierarchical class index...
Generating member index...
Generating file index...
Generating file member index...
Generating example index...
finalizing index lists...
writing tag file...
lookup cache used 0/65536 hits=0 misses=0
finished...

build/html にファイルが生成されていますので,index.html を開きます.

1564752341

index.md に記載されている {#mainpage} と書かれた部分が Main Page ボタンを押したとき,または,HTMLHELP形式でのホームボタンが押されたときに表示されるページとなります.

📌 ページの追加

新しいページを追加するときは src フォルダにファイルを作成します.最低限必要なのは \page コマンドです.

\page <page_name> ページ名

例えば,src/page.md というファイルを作成します.

\page sample サンプルページ

これでビルドすると

1564752885

となって,Related Pages というタブが追加され,そこにページが現れます.

📌 サブページの設定

あるページの子ページとして設定するには \subpage コマンドを使います.

\subpage <page_name>

例えば,サンプルページをトップページの子ページとして設定するにはトップページ(index.md)で

\subpage sample

を追加します.

# Hello Doxygen!    {#mainpage}
このドキュメントはDoxygenで作られています.
- \subpage sample

そうすると

1564753099

となります.このようにページをどんどん追加していってドキュメントを構築していくのが基本となります.

📌 Doxygen におけるMarkdownのサポート

**__ の太字・斜字はほぼ使えません.代わりに \b\em<b>, <em> のようにDoxygenの特殊コマンドかHTMLタグを使います.それ以外はほとんど対応しています.

📌 画像

Markdown形式でも問題なく使用できます.

![<alt>](<filename>)

また,Doxygenの特殊コマンドは

\image <target> <filename>

となります.<target> には htmllatex を指定します.<filename> はファイル名を指定します.このファイル名は IMAGE_PATH で指定したフォルダに入っている必要があります.Doxygenの特殊コマンド \image ではセンタリングされ,Markdown形式ではセンタリングされません.

VSCodeの拡張機能で PasteImage というのがあります.これはクリップボードに入っている画像を特定のフォルダに保存してそのファイル名を自動で入れてくれるものです.

PasteImage の Workspace 設定で次のようにすると便利です.

1564754072

これで,PasteImage を実行すれば src/images フォルダにクリップボードにある画像が作成され,コマンドが編集位置に挿入されます.

次に DoxyfileIMAGE_PATHsrc/images を設定します.

DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = Doxygenによるドキュメント作成
OUTPUT_DIRECTORY = 
OUTPUT_LANGUAGE = Japanese-en
ALIASES = 
MARKDOWN_SUPPORT = YES
GENERATE_HTML = YES
HTML_OUTPUT = html
INPUT = ../src/index.md ../src/second_index.md ../src
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.md
IMAGE_PATH = ../src/images

📌 数式

DoxygenではLatexMathjaxを使った数式表示に対応しています.Mathjaxの方がお手軽ですが,インターネット接続が必要で,かつ処理が遅いというデメリットがあります.Latexの場合はTeXLiveおよびGsを準備すれば使用することができます.

数式はインラインの場合 \f$ で括ります.ブロックの場合は $$\f[ , \f] となります.またTeXの環境コマンドを使う場合は

\f{align}{
    ...
\f}

とします.

Mathjax

Mathjaxを有効にするには USE_MATHJAX=YES の設定を追加します.また,拡張機能として AMSmathAMSsymbols を有効にします.あとは,MathjaxのURLを設定します.

USE_MATHJAX = YES
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1
MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols

例えば,次のようなページを作成すると

\page math 数式

\f$ L = L\cdot N\cos\theta \f$

$$
L = L\cdot N\cos\theta
$$

\f[
    L = L\cdot N\cos\theta
\f]

次のように表示されます.

1564770131725

LaTeX

USE_MATHJAX=NOに指定すると数式はLaTeXで作成されます.TeX環境はTeXLiveをインストールすることをオススメしますが,Doxygenで使う場合はGhostScript(32ビット)は別途インストールする必要があります.適当な場所にインストールして,gsview32c.exeが通るようにパスを設定します.また,EXTRA_PACKAGES=amsmathを指定します.

USE_MATHJAX = NO
EXTRA_PACKAGES = amsmath

📌 Admonition

doxygenにはメモや警告の特殊コマンドがあります.

\note これはノートです.
\warning これは警告です.

1564771395014

📌 コードブロック

` ` ` で囲むか,Doxygenの特殊コマンド \code, \endcode で囲みます.特殊コマンドの場合の言語指定は \code{.cpp}, \endcode というように書きます.Doxygenがサポートしている言語は次のものだけです.

IDL, Java, Javascript, C#, C, C++, D, PHP, Objective-C, Python, Fortran

📌 整形済みテキスト

インデントを加えることで整形済みテキストとなります.ただし,Doxygenの特殊コマンドが優先されてしまうため,整形済みテキストの中に特殊コマンドが含まれている場合は \code{.unparsed}, \endcode か,\verbatim, \endverbatim を使います.

📌 段落

\par コマンドで段落の開始を指定することができます.\par <title> で段落のタイトルをつけることができます.これを使って定義リストのような感じにすることもできます.

\par Doxygen:
Doxygen is the de facto standard tool for generating documentation from annotated C++ sources

\par
Doxygenはプログラムのコメントに書かれた情報をもとにドキュメントを作成するツールです.Doxygenはプログラムコードを解析する機能に特化していますが,Markdown形式に対応していて,コメントの中に含めることができます.また,プログラムコードのコメントでなければいけないのかというとそうでもなく,Markdownファイルのみから文書を作成することができます.さらにMarkdown形式とDoxygenの特殊コマンドを同時に使えます.ただし,それによる弊害も多少あり,使いづらいところもあったりします.

1565113031461

📌 絵文字

doxygenは絵文字をサポートしています.次のように記述します.

\emoji :smile:

1564772381975

📌 Dot

グラフ構造を記述するDot言語をサポートしています.使用するには Graphviz をインストールして dot.exe の場所を DOT_PATH で指定し,HAVE_DOT=YES とします.

HAVE_DOT = YES
DOT_PATH = "C:\Program Files (x86)\graphviz-2.34\bin\dot.exe"

使用するには dot言語で書かれた部分を \dot, \enddot で囲みます.

\dot
digraph G {
    a -> c;
    a -> b;
    b -> c [constraint=false];
}
\enddot

\dot
digraph example {
    node [shape=record, fontname=Helvetica, fontsize=10];
    b [ label="class B" URL="\ref B"];
    c [ label="class C" URL="\ref C"];
    b -> c [ arrowhead="open", style="dashed" ];
}
\enddot

1564772968557

📌 PlantUML

PlantUML を使ってUML図を生成することができます.ダウンロードしたファイルを適当な場所に配置して,PlantUML.jar の場所を PLANTUML_JAR_PATH で指定します.

PLANTUML_JAR_PATH = C:\PlantUML\plantuml.jar

そして,\startuml および \enduml で囲みます.

\startuml
Object <|-- ArrayList

Object : equals()
ArrayList : Object[] elementData
ArrayList : size()

\enduml

1564775477574

UML図は yUML といったサービスも利用できます.

📌 他のページの参照

Doxygenの特殊コマンド \ref を使います.

\ref <page_name>

📌 目次の生成

ページの右上に目次を表示することができます.Doxygenの特殊コマンド \tableofcontents を使います.

\tableofcontents{html}

目次に列挙される見出しの深さは TOC_INCLUDE_HEADINGS で指定します.

TOC_INCLUDE_HEADINGS = 2

1564777970814

📌 ファイルの埋め込み

例えば PDFファイルを埋め込んでリンクとして参照することができます.IEで開かれ,大体PDFビューアが起動することになります.

埋め込むファイルは HTML_EXTRA_FILES で指定します.ここでは埋め込むファイルを extra フォルダに入れることにします.例えば hoge.pdf という文書を埋め込む場合

HTML_EXTRA_FILES = ../extra/hoge.pdf

として,ドキュメントからは

<a href="hoge.pdf" target="_blank">hoge (PDF)</a>

とします.

📌 HTMLHELPの作成

HTMLHELP(CHM)形式の作成には HTML Help Workshop が必要です.適当な場所にインストールして HHC_LOCATIONhhc.exe の場所を指定します.また,GENERATE_HTMLHELP=YES と設定します.また,出力ファイル名を CHM_FILE で設定します.

GENERATE_HTMLHELP = YES
HHC_LOCATION = "C:\Program Files (x86)\HTML Help Workshop\hhc.exe"
CHM_FILE = "doc.chm"

1564776149959

どうやら文字化けしてしまっているようです.CHM_INDEX_ENCODINGShift_JISを指定します.

1564776268158

これで HTMLHELP(CHM) 形式の作成が出来るようになりました.目次の部分は文字化けが発生していませんが,検索画面では文字化けしてしまいます.これはちょっと解決策がわかっていません.

📌 カスタマイズ

ここから自分なりの見栄えがするドキュメントを作成するためにカスタマイズをしていきます.

コンテンツ側のタブナビゲーションを削除

まず,CHMファイルでは左側に索引があるので,コンテンツのタブは削除します.これは DISABLE_INDEX=YESとします.

1564776607095

スタイル

スタイルシートやヘッダー,フッターをカスタマイズして見た目を変更します.それぞれ HTML_HEADER, HTML_FOOTER, HTML_STYLESHEET で指定します.Doxygenでは標準のファイルがあって,次のコマンドを実行すると生成することができます.

doxygen -w html header.html footer.html stylesheet.css

これを実行すると

error: When enabling GENERATE_HTMLHELP the search engine (SEARCHENGINE) should be disabled. I'll do it for you.

とエラーが表示されるかもしれません.その場合はエラー内容の通りに SEARCHENGINE=NO とします.
それぞれ,build フォルダに格納して設定します.

HTML_HEADER = header.html
HTML_FOOTER = footer.html
HTML_STYLESHEET = stylesheet.css

見出しスタイル

まずは見出しのスタイルを変更します.先程 stylesheet.css を作成しましたが,スタイルシートは標準のものを変更せずにカスケードでカスタマイズすることが推奨されています.そのために HTML_EXTRA_STYLESHEET というのがあります.ここでは custom.css というのを作成して設定することにします.

HTML_EXTRA_STYLESHEET = custom.css

見出しのスタイルは標準だと次のようになっています.

1564778816447

custom.css を次のようにします.

div.contents div.textblock h1,
div.contents div.textblock h2,
div.contents div.textblock h3,
div.contents div.textblock h4,
div.contents div.textblock h5 {
	font-family: "Roboto Slab", "Noto Sans JP", "Georgia", Arial, sans-serif;
	font-weight: bold;
	color: rgb(0,113,188);
}

div.contents div.textblock h1 {
	font-size: 28px;
	margin-top: 48px;
	margin-bottom: 32px;
}

div.contents div.textblock h2 {
	font-size: 20px;
	margin-top: 32px;
	margin-bottom: 8px;
    padding-bottom: 4px;
	border-bottom: 1px dotted #888;
}

1564779192350

FontAwesome

見出しにFontAwesomeのアイコンをつけます.FontAwesomeを使うには header.html で css ファイルを追加します.

$extrastylesheet
<link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet" type="text/css"/>

見出しのアイコンは css を使って追加します.custom.css に以下を追加します.

div.contents div.textblock h1:before {
	font-family: "Font Awesome 5 Free";
	font-weight: 900;
	content: '\f0a9';
	padding-right: 8px;
}

1564779486256

プロジェクトタイトルの削除

コンテンツ側の一番上にプロジェクトタイトルが常に表示されています.不要なので非表示にしましょう.custom.cssに追記します.

#titlearea
{
    visibility: hidden;
    height: 0px;
}

1564779564480

コンテンツの幅を広げる

デフォルトでは幅が狭かったので,1000近くまで拡げます.custom.css に以下を追加します.

div.contents {
	width: 1000px;
}

div.header {
	width: 1000px;
}

フォントの変更

本文のフォントに Noto Sans JP, 見出しのフォントに Roboto Slab, 整形済みテキストに Roboto Mono を指定します.Webフォントを使いますので,header.htmlに以下を追加します.

<link href="https://fonts.googleapis.com/css?family=Noto+Sans+JP|Roboto+Mono|Roboto+Slab&display=swap&subset=japanese" rel="stylesheet"> 

次に custom.css に以下を追加します.

body, table, div, p, dl, li, title,
#nav-tree .label {
	font-family: "Noto Sans JP", "Roto", Consolas, "Courier New", sans-serif;
}

1564780749185

整形済みテキスト,コードのフォントを変更します.custom.cssに以下を追加します.

div.contents div.textblock code:not(.hljs) {
	font-family: "Roboto Mono", Consolas, "Courier New", courier, monospace;
	color: #d63200;
	border-radius: 2px;
	padding: 3px 5px;
	margin: 0px 2px;
	background-color: #f8f8f8;	
}
div.contents div.textblock code.hljs,
div.contents div.textblock pre.fragment {
	font-family: "Roboto Mono", Consolas, "Courier New", courier, monospace;
}

div.contents div.textblock div.fragment {
	padding: 8px;
	background-color: #f0f0f0;
}

div.contents div.textblock div.line {
	font-family: "Roboto mono", Consolas, "Courier New", courier, monospace;
	font-size: 11pt;
	line-height: 1.3em;
}

新しいウィンドウで開くリンクにアイコンを追加

target="_blank" が指定しているリンクではFontAwesomeのアイコンを後ろにつけます.custom.css に以下を追加します.

div.contents div.textblock a[target="_blank"]:after {
	font-family: "Font Awesome 5 Free";
	font-weight: 900;
	content: '\f35d';
	margin: 0px 3px;
	font-size: 0.9em;
}

1564781284304

目次のフォントを変える

CHMファイルの目次のフォントを「メイリオ」に変更します.これには index.hhp ファイルを書き換えなければいけません.今回は python を使って書き換えます.build/hhpfix.py を作成し次のようにします.

import sys
import fileinput

def main(argv=None):
    for line in fileinput.input('html/index.hhp', inplace=True, backup='.bak'):
        print(line.strip())
        if line.startswith('[OPTIONS]'):
            print('Default Font=メイリオ,9,128')
    return 0

if __name__ == '__main__':
    sys.exit(main())

ビルドタスクで処理する時は,まず HHC_LOCATION に空の値を設定して,doxygenのビルドではCHMファイルを生成しないようにします.次に python hhpfixbuild フォルダで実行します.あとは,以下を実行して CHM ファイルを作成します.

"C:\Program Files (x86)\HTML Help Workshop\hhc.exe" html\index.hhp

1564781636714

シンタックスハイライト

Doxygenは標準でシンタックスハイライト機能を持っていますが,IDL, Java, Javascript, C#, C, C++, D, PHP, Objective-C, Python, Fortranしか対応していません.例えばGLSLC++形式に対応させる場合はEXTENSION_MAPPINGを使います.

EXTENSION_MAPPING = .glsl=C++

それ以外の言語に対応させる場合は拡張します.ここではhighlight.js を使ってシンタックスハイライトを行います.リンク先からダウンロードしたファイルを展開して出来たフォルダを任意の場所に配置して,以下の内容をheader.htmlに追加します.

<link href="$relpath^monokai.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="$relpath^highlight.pack.js"></script>
<script>
$(function() {
    $("div.fragment").each(function(i,node) {
        $(node).removeClass("fragment");
        hljs.highlightBlock(node);
    });
});
</script>

シンタックスハイライトのテーマはstylesフォルダに色々ありますので好きなものを選びます.このドキュメントではmonokaiを指定しています.次にmonokai.csshighlight.pack.jsを出力先に含めないといけないので HTML_EXTRA_FILES に追記します.

HTML_EXTRA_FILES = highlight.pack.js monokai.css

これで` ` `\codeにhighlight.jsを使ったシンタックスハイライトが適用されますが,言語を指定することができず,highlight.jsが自動認識した言語になります.それでは扱いづらいので,何とか指定できる方法を考えます.doxygenではコードブロックは <div class="fragment">...</div> に置き換わるので,このdivタグのさらに親要素を追加し,その親要素で言語を指定します.それをjavascriptで取得してhighlight.jsに渡します.まずはALIASEShl,endhlというのを追加して親要素を追加します.

ALIASES += hl{1}="<div class=\"hl \1\">\code"
ALIASES += endhl="\endcode</div>"

使い方はcode,endcodeの代わりにhl,endhlを使い,言語は

\hl{html}
...
\endhl

という風に指定します.そして header.html に追加した部分を次のように書き換えます.

<link href="$relpath^monokai.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="$relpath^highlight.pack.js"></script>
<script>
$(function() {
    function htmlspecialchars(str){
      return (str + '').replace(/&/g,'&amp;')
                       .replace(/"/g,'&quot;')
                       .replace(/'/g,'&#039;')
                       .replace(/</g,'&lt;')
                       .replace(/>/g,'&gt;'); 
    }

    $("div.fragment").each(function(i,node) {
        var lang="";
        var hlnode = $(node).parent('div.hl');
        if (hlnode.length > 0) {
            lang = " class=\"" + hlnode.attr('class').split(' ')[1] + "\"";
        }
        var text = "";
        $(node).find('div.line').each(function(i,elem) {
            text += $(elem).text() + "\n";
        });
        $(node).empty();
        $(node).html("<pre><code"+lang+">"+htmlspecialchars(text)+"</code></pre>");
        hljs.configure({useBR: false});
        hljs.highlightBlock($(node).find("code")[0]);
        $(node).children("pre").unwrap();
        if (hlnode.length>0) $(hlnode).children("pre").unwrap();
    });
});
</script>

これで次のようにシンタックスハイライトが行われます.

1564781874180

コードのコピー機能

よくあるコードをクリップボードにコピーするボタンを設置します.
それには clipboard.jstoastr.js を使います.また,コピーのアイコンは適当な場所から持ってきます.まず,header.html を次のように変更します.

<script>
$(function() {
    function htmlspecialchars(str){
      return (str + '').replace(/&/g,'&amp;')
                       .replace(/"/g,'&quot;')
                       .replace(/'/g,'&#039;')
                       .replace(/</g,'&lt;')
                       .replace(/>/g,'&gt;'); 
    }

    var counter = 1;
    $("div.fragment").each(function(i,node) {
        var lang=" class=\"code\"";
        var hlnode = $(node).parent('div.hl');
        if (hlnode.length > 0) {
            lang = " class=\"code " + hlnode.attr('class').split(' ')[1] + "\"";
        }
        var text = "";
        $(node).find('div.line').each(function(i,elem) {
            text += $(elem).text() + "\n";
        });
        var id = ' id="code'+counter+'"';
        var cbtarget = ' data-clipboard-target="#code'+counter+'"';
        counter++;
        var btn = '<button class="cpybtn" '+cbtarget+'><img class="clippy" src="$relpath^clippy.svg" width="15"/></button>'
        $(node).empty();
        $(node).html("<pre>"+btn+"<code"+id+lang+">"+htmlspecialchars(text)+"</code></pre>");
        hljs.configure({useBR: false});
        hljs.highlightBlock($(node).find("code")[0]);
        $(node).children("pre").unwrap();
        if (hlnode.length>0) $(hlnode).children("pre").unwrap();
    });

    new Clipboard('.cpybtn');
    $(".cpybtn").click(function(){
        toastr.options.positionClass = "toast-top-center";
        toastr.success('&nbsp;&nbsp; Copied to the clipboard');
    });
});
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.5/clipboard.min.js"></script>

ここでは clippy.svg という画像を使っています.これを HTML_EXTRA_FILES に追加します.次に custom.css に次を追加します.

div.contents div.textblock pre {
	position:relative;
	overflow-x: auto;
}

div.contents div.textblock pre > button.cpybtn {
	top: 4px;
	right: 4px;
	display: inline-block;
	position:absolute;
	padding: 2px 6px;
	border: 1px solid #d5d5d5;
	border-radius: 3px;
	cursor: pointer;
	background-color: #eee;
	background-image: linear-gradient(#fcfcfc, #eee);
	line-height: 20px;
	font-weight: 700;
	font-size: 16px;
}

img.clippy {
	position: relative;
	left: 1px;
	top: 1px;
}


pre > div.cpybtn {
	display:block;
	color: #fff;
	padding: 0px 5px;
	width: auto;
	height: auto;
	font-size: 95%;
	font-weight: bold;
	margin: 10px;
	background: #2196F3;
	cursor :pointer;
	float:right;
	border-radius: 3px;
}
pre > div.cpybtn:after{
	content: "";
	clear: both;
}
pre > div.cpybtn:hover {
	background :#85baef;
}

これでコードの右上にコピーボタンが表示されます.

1564782319856

コピーボタンを押すと,コピーしたことを通知します.

1564783054319

ここでclippy.svgがHTMLHELP(CHM)にファイルが含まれない問題が発生しました.そこで,hhpfix.py で対応することにします.hhpfix.pyを次のようにします.

import sys
import fileinput

def main(argv=None):
    for line in fileinput.input('html/index.hhp', inplace=True, backup='.bak'):
        print(line.strip())
        if line.startswith('[OPTIONS]'):
            print('Default Font=メイリオ,9,128')
        if line.startswith('[FILES]'):
            print('clippy.svg')
    return 0

if __name__ == '__main__':
    sys.exit(main())

📌 最終ファイル

Doxyfile
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = Doxygenによるドキュメント作成
OUTPUT_DIRECTORY = 
OUTPUT_LANGUAGE = Japanese-en
ALIASES = 
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 2
ALPHABETICAL_INDEX = YES
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_HEADER = header.html
HTML_FOOTER = footer.html
HTML_STYLESHEET = stylesheet.css
HTML_EXTRA_STYLESHEET = custom.css
INPUT = ../src/index.md ../src/second_index.md ../src
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.md
IMAGE_PATH = ../src/images
USE_MATHJAX = YES
MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1
MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
HAVE_DOT = YES
DOT_PATH = E:\bin\graphviz-2.34\bin\dot.exe
PLANTUML_JAR_PATH = E:\bin\plantuml\plantuml.jar
GENERATE_HTMLHELP = YES
HHC_LOCATION = "C:\Program Files (x86)\HTML Help Workshop\hhc.exe"
CHM_FILE = "index.chm"
CHM_INDEX_ENCODING = Shift_JIS
DISABLE_INDEX = YES
SEARCHENGINE = NO
header.html
<!-- HTML header for doxygen 1.8.15-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
<link href="https://fonts.googleapis.com/css?family=Noto+Sans+JP|Roboto+Mono|Roboto+Slab&display=swap&subset=japanese" rel="stylesheet"> 
<link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet" type="text/css"/>
<link href="$relpath^monokai.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="$relpath^highlight.pack.js"></script>
<script>
$(function() {
    function htmlspecialchars(str){
      return (str + '').replace(/&/g,'&amp;')
                       .replace(/"/g,'&quot;')
                       .replace(/'/g,'&#039;')
                       .replace(/</g,'&lt;')
                       .replace(/>/g,'&gt;'); 
    }

    var counter = 1;
    $("div.fragment").each(function(i,node) {
        var lang=" class=\"code\"";
        var hlnode = $(node).parent('div.hl');
        if (hlnode.length > 0) {
            lang = " class=\"code " + hlnode.attr('class').split(' ')[1] + "\"";
        }
        var text = "";
        $(node).find('div.line').each(function(i,elem) {
            text += $(elem).text() + "\n";
        });
        var id = ' id="code'+counter+'"';
        var cbtarget = ' data-clipboard-target="#code'+counter+'"';
        counter++;
        var btn = '<button class="cpybtn" '+cbtarget+'><img class="clippy" src="$relpath^clippy.svg" width="15"/></button>'
        $(node).empty();
        $(node).html("<pre>"+btn+"<code"+id+lang+">"+htmlspecialchars(text)+"</code></pre>");
        hljs.configure({useBR: false});
        hljs.highlightBlock($(node).find("code")[0]);
        $(node).children("pre").unwrap();
        if (hlnode.length>0) $(hlnode).children("pre").unwrap();
    });

    new Clipboard('.cpybtn');
    $(".cpybtn").click(function(){
        toastr.options.positionClass = "toast-top-center";
        toastr.success('&nbsp;&nbsp; Copied to the clipboard');
    });
});
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.5/clipboard.min.js"></script>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->

<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <!--BEGIN PROJECT_LOGO-->
  <td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
  <!--END PROJECT_LOGO-->
  <!--BEGIN PROJECT_NAME-->
  <td id="projectalign" style="padding-left: 0.5em;">
   <div id="projectname">$projectname
   <!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
   </div>
   <!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
  </td>
  <!--END PROJECT_NAME-->
  <!--BEGIN !PROJECT_NAME-->
   <!--BEGIN PROJECT_BRIEF-->
    <td style="padding-left: 0.5em;">
    <div id="projectbrief">$projectbrief</div>
    </td>
   <!--END PROJECT_BRIEF-->
  <!--END !PROJECT_NAME-->
  <!--BEGIN DISABLE_INDEX-->
   <!--BEGIN SEARCHENGINE-->
   <td>$searchbox</td>
   <!--END SEARCHENGINE-->
  <!--END DISABLE_INDEX-->
 </tr>
 </tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->
custom.css
body, table, div, p, dl, li, title,
#nav-tree .label {
	font-family: "Noto Sans JP", "Roto", Consolas, "Courier New", sans-serif;
}

div.contents div.textblock h1,
div.contents div.textblock h2,
div.contents div.textblock h3,
div.contents div.textblock h4,
div.contents div.textblock h5 {
	font-family: "Roboto Slab", "Noto Sans JP", "Georgia", Arial, sans-serif;
	font-weight: bold;
	color: rgb(0,113,188);
}

div.contents div.textblock h1 {
	font-size: 28px;
	margin-top: 48px;
	margin-bottom: 32px;
}

div.contents div.textblock h2 {
	font-size: 20px;
	margin-top: 32px;
	margin-bottom: 8px;
    padding-bottom: 4px;
	/* border-bottom: 1px solid #ccccdd; */
	border-bottom: 1px dotted #888;
}

div.contents div.textblock h1:before {
	font-family: "Font Awesome 5 Free";
	font-weight: 900;
	content: '\f0a9';
	padding-right: 8px;
}

div.contents div.textblock code:not(.hljs) {
	font-family: "Roboto Mono", Consolas, "Courier New", courier, monospace;
	color: #d63200;
	border-radius: 2px;
	padding: 3px 5px;
	margin: 0px 2px;
	background-color: #f8f8f8;	
}

div.contents div.textblock code.hljs,
div.contents div.textblock pre.fragment {
	font-family: "Roboto Mono", Consolas, "Courier New", courier, monospace;
}

div.contents div.textblock div.fragment {
	padding: 8px;
	background-color: #f0f0f0;
}

div.contents div.textblock div.line {
    font-family: "Roboto Mono", Consolas, "Courier New", courier, monospace;
	font-size: 11pt;
	line-height: 1.3em;
}

#titlearea
{
    visibility: hidden;
    height: 0px;
}

div.contents div.textblock a[target="_blank"]:after {
	font-family: "Font Awesome 5 Free";
	font-weight: 900;
	content: '\f35d';
	margin: 0px 3px;
	font-size: 0.9em;
}


div.contents div.textblock pre {
	position:relative;
	overflow-x: auto;
}

div.contents div.textblock pre > button.cpybtn {
	top: 4px;
	right: 4px;
	display: inline-block;
	position:absolute;
	padding: 2px 6px;
	border: 1px solid #d5d5d5;
	border-radius: 3px;
	cursor: pointer;
	background-color: #eee;
	background-image: linear-gradient(#fcfcfc, #eee);
	line-height: 20px;
	font-weight: 700;
	font-size: 16px;
}

img.clippy {
	position: relative;
	left: 1px;
	top: 1px;
}


pre > div.cpybtn {
	display:block;
	color: #fff;
	padding: 0px 5px;
	width: auto;
	height: auto;
	font-size: 95%;
	font-weight: bold;
	margin: 10px;
	background: #2196F3;
	cursor :pointer;
	float:right;
	border-radius: 3px;
}
pre > div.cpybtn:after{
	content: "";
	clear: both;
}
pre > div.cpybtn:hover {
	background :#85baef;
}

📌 最後に

ドキュメント作成の1つの方法として試してみてはいかがでしょうか.
参考になれば幸いです.