技術文書やレポートの作成において、Markdownは簡潔で読みやすい記法として広く使われています。VS Code(Visual Studio Code)の拡張機能「Markdown PDF」を使えば、MarkdownファイルをPDFに簡単に変換でき、CSSを使った見た目のカスタマイズも可能です。本記事では、セットアップから実践的な使い方まで、包括的に解説します。

環境構築

1. 必要なツールのインストール

VS Codeのインストール

公式サイト から最新版をダウンロードしてインストールします。

Markdown PDF拡張機能のインストール

  1. VS Codeを起動
  2. 左サイドバーの拡張機能アイコンをクリック(またはCtrl+Shift+X)
  3. 「Markdown PDF」を検索
  4. 拡張機能「Markdown PDF (yzane)」をインストール

基本的な使い方

PDFへの変換方法

  1. .mdファイルを開く
  2. ファイル内で右クリック
  3. Markdown PDF: Export (pdf) を選択
  4. 同じフォルダに.pdfファイルが生成される

他の形式への変換

Markdown PDFは以下の形式にも対応しています:

  • HTML
  • PNG
  • JPEG

右クリックメニューから各形式を選択できます。

CSSでスタイルをカスタマイズ

1. CSSファイルの作成

markdown-pdf.cssを作成:

https://gist.github.com/sarap422/03d76cfbad7fddd7c773b80b368ec463
/**
* ▼CSS: markdown-pdf.css
* (Markdown → PDF用)
***************************************/
	⌇
	⌇
/* list
-------------------------------------- */
body ul,
body ol {
	/* padding  上  右  下  左 */
	padding: 0.5rem 0rem 0.5rem 2rem;
}

body ol ol,
body ul ol {
	list-style-type: lower-roman;
}

body ul ul ol,
body ul ol ol,
body ol ul ol,
body ol ol ol {
	list-style-type: lower-alpha;
}

body li {
	margin-top: .125em;
	margin-bottom: .125em;
}


/* table
-------------------------------------- */
/* table(collapse) */
body table {
	width: 100%;
	max-width: 100%;
	border-collapse: collapse;
	border-spacing: 0;
	background: var(--c-base, hsl(223, 6%, 100%));
	border-top: 1px solid var(--c-text-300, hsl(223, 6%, 74%));
	border-left: 1px solid var(--c-text-300, hsl(223, 6%, 74%));
	font-variant: tabular-nums;
}

body table:after {
	content: "";
	display: block;
	clear: both
}

/* inline-block の余白削除 */
body table:not(.token),
body table:not(.token) thead,
body table:not(.token) tbody,
body table:not(.token) tfoot,
body table:not(.token) tr {
	font-size: 0;
	letter-spacing: 0;
	width: 100%;
	max-width: 100%;
}

/* font-size(デフォルト) */
body table caption,
body table th,
body table td {
	line-height: clamp(1.44em, calc(1.44em + ((1vw - 0.225em) * 0.55)), 1.66em);
	font-size: clamp(0.8125rem, calc(0.8125rem + ((1vw - 0.225rem) * 0.3125)), 0.9375rem);
}

body table th,
body table td {
	border-bottom: 1px solid var(--c-text-300, hsl(223, 6%, 74%));
	border-right: 1px solid var(--c-text-300, hsl(223, 6%, 74%));
}

body table th {
	padding: 0.5pc 1rem 0.5pc 1rem;
	background: var(--c-secondary, hsl(223, 47%, 97%))
}

body table td {
	padding: 0.5pc 1rem 0.5pc 1rem
}

body table thead th,
body table thead td {
	background: var(--c-primary, hsl(223, 62%, 23%));
	color: #FFF
}

/* img(borderを付ける) */
body img {
	border: 2px solid var(--c-text-300, hsl(223, 6%, 74%));
}


/* Headings(h1, h2, h3, h4, h5, h6)
-------------------------------------- */
body h1,
body h2,
body h3,
body h4,
body h5,
body h6 {
	position: relative;
	display: block;
	z-index: 10;
	width: 100%;
	max-width: 100%;
	margin-bottom: 0.5rem;
}

/* Headings(最初以外) */
body h2:nth-child(n+2) {
	margin-top: 2em;
}

body h3:nth-child(n+2) {
	margin-top: 1.17em;
}

body h4:nth-child(n+2) {
	margin-top: 1em;
}

body h5:nth-child(n+2) {
	margin-top: 0.83em;
}

body h6:nth-child(n+2) {
	margin-top: 0.67em;
}

/* タイトル */
body h1 {
	position: relative;
	display: flow-root;
	/* margin: 上  横  下 */
	margin: 0.5rem auto 2.5rem;
	padding: 0.5rem;
	line-height: 1.44;
	letter-spacing: clamp(0.03em, calc(0.03em + ((1vw - 0.225em) * 0.15)), 0.09em);
	font-size: clamp(1.025rem, calc(1.025rem + ((1vw - 0.225rem) * 0.7188)), 1.3125rem);
	font-weight: 500;
	text-align: center;
	border-color: var(--c-primary-pale, hsl(223, 24%, 47%));
}

body h1[class*="ta-center"] {
	text-indent: clamp(0.05em, calc(0.05em + ((1vw - 0.225em) * 0.25)), 0.15em);
}

body h1:before,
body h1:after {
	position: absolute;
	z-index: 20;
	pointer-events: none;
	width: 100%;
	margin: auto;
	content: "";
}

body h1:before {
	top: 0px;
	right: 0px;
	left: 0px;
	border-top: 1px solid;
	border-color: inherit;
}

body h1:after {
	right: 0px;
	bottom: 0px;
	left: 0px;
	border-bottom: 1px solid;
	border-color: inherit;
}

/* h2〜h6 */
body h2 {
	line-height: 1.33;
	letter-spacing: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
	font-size: clamp(1.05rem, calc(1.05rem + ((1vw - 0.225rem) * 1.125)), 1.5rem);
	font-weight: 500;
	/* padding  上  横  下 */
padding: 0.125em 0.25rem 0em;
	background: var(--c-primary-100, hsl(223, 50%, 93%));
	border-left: 0.5rem solid var(--c-primary, #2a417e);
}

body h2 {
	line-height: 1.33;
	letter-spacing: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
	font-size: clamp(1.05rem, calc(1.05rem + ((1vw - 0.225rem) * 1.125)), 1.5rem);
	font-weight: 500;
/* padding  上  横  下 */
padding: 0.125em 0.25rem 0em;
	background: var(--c-primary-100, hsl(223, 50%, 93%));
	border-left: 0.5rem solid var(--c-primary, #2a417e);
}

body h2[class*="ta-center"] {
	text-indent: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
}

body h3 {
	line-height: 1.33;
	letter-spacing: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
	font-size: clamp(1rem, calc(1rem + ((1vw - 0.225rem) * 0.4688)), 1.1875rem);
	font-weight: 500;
	border-bottom: 1px solid var(--c-text-300, hsl(223, 6%, 74%));
	color: var(--c-primary, #2a417e);
}

body h3[class*="ta-center"] {
	text-indent: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
}

body h4 {
	line-height: 1.33;
	letter-spacing: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
	font-size: clamp(0.9563rem, calc(0.9563rem + ((1vw - 0.225rem) * 0.2656)), 1.0625rem);
	font-weight: 500;
}

body h4[class*="ta-center"] {
	text-indent: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
}

body h5 {
	line-height: 1.33;
	letter-spacing: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
	font-size: clamp(0.8750rem, calc(0.8750rem + ((1vw - 0.225rem) * 0.1563)), 0.9375rem);
	font-weight: 500;
}

body h5[class*="ta-center"] {
	text-indent: clamp(0.01em, calc(0.01em + ((1vw - 0.225em) * 0.05)), 0.03em);
}

body h6 {
	line-height: 1.33;
	letter-spacing: 0;
	font-size: clamp(0.7500rem, calc(0.7500rem + ((1vw - 0.225rem) * 0.1563)), 0.8125rem);
	font-weight: 500;
}

body h6[class*="ta-center"] {
	text-indent: 0;
}

/* .separator(区切り線) */
body hr {
	display: block;
	position: relative;
	width: 100%;
	clear: both;
	border: 0;
	border-color: var(--c-text-300, hsl(223, 6%, 74%));
	background-color: var(--c-text-300, hsl(223, 6%, 74%));
	height: 4px;
	-webkit-mask: repeat-x 50% 50%;
	-webkit-mask-image: url('data:image/svg+xml;charset=utf8,');
	mask: repeat-x 50% 50%;
	mask-image: url('data:image/svg+xml;charset=utf8,');
	margin-top: 3rem;
}

body blockquote {
	margin-top: 1.25rem;
	/* padding  上  横  下 */
	padding: 0.75rem 0.5rem 0.75rem;
	line-height: 1.66;
	font-size: 14px;
	background: var(--c-base, hsl(223, 6%, 100%));
	color: var(--c-text-500, hsl(223, 6%, 53%));
	border-left: 4px solid var(--c-text-200, hsl(223, 6%, 84%));
}

2. 設定ファイル(settings.json)で設定

VS Codeの設定から直接設定する場合:

{
  "markdown-pdf.displayHeaderFooter": false,
  "markdown-pdf.includeDefaultStyles": false,
  "markdown-pdf.styles": [
    "/Users/[ユーザー名]/Library/Application Support/css/markdown-pdf.css"
  ],
  "markdown-pdf.type": ["pdf"],
  "markdown-pdf.outputDirectory": "./output",
  "markdown-pdf.convertOnSave": false
}

改ページの設定

基本的な改ページ

任意の位置で改ページするには、以下のHTMLタグを挿入:

# 第1章
内容...

<div style="page-break-after:always"></div>

# 第2章
新しいページから始まります

改ページのバリエーション

<!-- ページの前で改ページ -->
<div style="page-break-before:always"></div>

<!-- ページの後で改ページ -->
<div style="page-break-after:always"></div>

<!-- 必要に応じて改ページを避ける -->
<div style="page-break-inside:avoid"></div>

CSSクラスを使った改ページ

CSSファイルに定義:

.pgbr-after {
  page-break-after: always;
}

.pgbr-avoid {
  page-break-inside: avoid;
}

Markdownで使用:

<div class="pgbr-after"></div>

ヘッダー・フッターの設定

ページ番号の表示

  1. VS Codeの設定を開く(Ctrl + ,)
  2. 検索欄に「footer」と入力
  3. Markdown-pdf: Display Header Footer にチェック

カスタムヘッダー・フッター

settings.jsonで詳細設定:

{
  "markdown-pdf.displayHeaderFooter": true,
  "markdown-pdf.headerTemplate": "<div style='font-size: 9px; margin-left: 1cm;'>My Document</div>",
  "markdown-pdf.footerTemplate": "<div style='font-size: 9px; margin: 0 auto;'><span class='pageNumber'></span> / <span class='totalPages'></span></div>"
}

便利なTips集

1. 目次の自動生成

Markdownで目次を作成:

## 目次
- [はじめに](#はじめに)
- [第1章](#第1章)
  - [1.1 概要](#11-概要)
  - [1.2 詳細](#12-詳細)
- [第2章](#第2章)

2. 印刷用スタイルの最適化

@media print {
  body {
    font-size: 11pt;
    line-height: 1.5;
  }
  
  h1 { font-size: 18pt; }
  h2 { font-size: 14pt; }
  h3 { font-size: 12pt; }
  
  /* URLを印刷時に表示 */
  a[href]:after {
    content: " (" attr(href) ")";
    font-size: 9pt;
    color: #666;
  }
  
  /* 改ページの制御 */
  h1, h2 {
    page-break-after: avoid;
  }
  
  table, figure {
    page-break-inside: avoid;
  }
}

3. 複数ファイルの一括変換

VS Codeのタスク機能を使用して複数ファイルを一括変換:

.vscode/tasks.jsonを作成:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Convert all MD to PDF",
      "type": "shell",
      "command": "echo 'Batch conversion started'",
      "problemMatcher": []
    }
  ]
}

4. Mermaidダイアグラムの活用

フローチャートやシーケンス図を含める:

```mermaid
graph TD
    A[開始] --> B{条件分岐}
    B -->|Yes| C[処理1]
    B -->|No| D[処理2]
    C --> E[終了]
    D --> E

### 7. 数式の表示(KaTeX)

数式を含める場合:

```markdown
インライン数式: $E = mc^2$

ブロック数式:
$$
\frac{d}{dx}\left( \int_{a}^{x} f(t)\,dt \right) = f(x)
$$

トラブルシューティング

よくある問題と解決策

1. 日本語が文字化けする

  • 解決: settings.jsonに追加
{
  "markdown-pdf.executablePath": "/path/to/chromium"
}

2. 画像が表示されない

  • 原因: 相対パスの問題
  • 解決: 画像パスを確認し、必要に応じて絶対パスを使用

3. 改ページが効かない

  • 原因: プレビューでは改ページは表示されない
  • 解決: PDF出力後に確認

VS CodeとMarkdown PDFを使えば、美しいPDFドキュメントを効率的に作成できます。CSSによるカスタマイズで、企業のブランドガイドラインに沿ったデザインも実現可能です。

この設定を基本として、プロジェクトの要件に応じてドキュメント作成環境をカスタマイズできます。