Markdown から HTML と PDF を作る

目次

講義記録や業務文書を書くにあたって,ひとつのファイルから HTML と PDF をそれなりの見た目で作れるように VSCodeMarkdown PDF を用いてテンプレートを作った。 これをまとめる。

背景

年度替わりにあたって,長らく情報の授業を持っていただいていた同僚が異動なさった。それに伴い,この1年は私が情報の授業を4単位分ほど担当することとなった。

情報を持つのは6年ぶりであり,カリキュラムも変わっている。 何より,生徒が1人ずつ端末を持っている。 そこで,復習しやすいようにウェブサイト上に講義記録を残すことにした。 少しでも手間が小さくなるよう,原稿は Markdown で書き VSCode によって HTML を作る。また,一部を印刷して配りたくなるかもしれないので,そのまま PDF にもできるようにしたい。

解決

Markdown PDF の導入

Markdown から HTML と PDF を作るには Markdown PDF を用いる。

VSCode を導入していれば,[拡張機能]から markdown pdf と検索してすぐに現れる。似た名前のものがいくつかあるが,ダウンロード数や評価を見ればよい。作者は yzane 氏である。

その他の VSCode 環境

Markdown の入力支援として Markdown All in One をインストールしている。

スニペットの入力支援として Easy Snippet をインストールしている。

これらは導入しなくとも差しつかえないが,一部の記述はこれらを踏まえている。

VSCode における Markdown PDF の設定

Footer Template はそのまま用いることとした。

Header Template は修正しやすいよう,style='display: none; のみを加えて非表示とした。

<div style="font-size: 9px; margin-left: 1cm;"> <span class='title' style='display: none;'></span></div> <div style="font-size: 9px; margin-left: auto; margin-right: 1cm; display: none;">%%ISO-DATE%%</div>

VSCode における Markdown のスニペット

改行位置の指定

印刷するにあたっての改行位置を指定するため,次のようにした。

// @prefix pagebreak-pdf
// @description 

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

スタイルシート

ここで,CSS を読み込む機能があるようだが,校内のファイル共有システムなどを踏まえ,オフライン環境でも扱えるようスタイルシートはファイルに直に書き込むこととした。いずれ見映えを変えたくなったときに使いまわしにくいことには気をつけたい。

  • 画面表示としても PDF としても見やすく,配置が大きく変わらないよう,間を取ってデザインした。
    • PDF は A4 用紙を想定している。
    • max-width: 600px;padding: 0 26px; を指定している。
    • 白黒印刷でも見やすいようデザインした。
  • em は圏点としている。em の意味からいえば,日本語では圏点がふさわしいだろう。
  • <!-- --> を取り除くことで MathJax を使えるようにしている。
// @prefix @css
// @description
<style>
/* CSS for Markdown to html/PDF 20251116 */
html, body {
  font-family: "BIZ UDMincho", serif;
  font-size: 15px;
  padding: 0 26px;
  line-height: var(--markdown-line-height, 22px);
  word-wrap: break-word;
  text-align: justify;
  overflow-wrap: break-word;
  hanging-punctuation: last allow-end;
  margin-left: auto;
  margin-right: auto;
}
body {
  line-height: 1.6;
  max-width: 600px;
}
h1, h2, h3, h4, h5, h6, p, ol, ul, pre {
  margin-top: 0;
}
h1, h2, h3, h4, h5, h6 {
  font-family: "BIZ UDGothic", sans-serif;
  font-weight: 500;
  margin-top: 24px;
  margin-bottom: 16px;
  line-height: 1.25;
}
h1 {
  font-size: 1.75em;
  font-weight: 700;
  margin-top: 0;
  padding-bottom: 0.1em;
  border-color: rgba(0, 0, 0, 0.35);
  border-bottom-width: 3px;
  border-bottom-style: double;
}
h2 {
  font-size: 1.35em;
  font-weight: 700;
  padding-bottom: 0.1em;
  border-color: rgba(0, 0, 0, 0.35);
  border-bottom-width: 1px;
  border-bottom-style: solid;
}
h3 {
  font-weight: 700;
  font-size: 1.25em;
}
h4 {
  font-size: 1.1em;
}
blockquote {
  margin: 0;
  padding: 0px 16px 0 10px;
  border-left-width: 5px;
  border-left-style: double;
  background: transparent;
  border-color: rgba(0, 0, 0, 0.35);
}
ul, ol {
  margin-top: 0;
  padding-left: 2em;
}
ul li:last-child, ul li li:last-child, ul li li li:last-child {
margin-bottom: 0.25em;
}
ul li li, ul li li li, ul li ul li, ul li ul li ul li {
margin-bottom: 0;
}
p {
  text-indent: 1em;
  margin-bottom: 1em;
}
p:has(img) {
  text-indent: 0em;
  margin-bottom: 1em;
}
ul img {
  padding-top: 3px;
  vertical-align: top;
}
ol img {
  padding-top: 3px;
  vertical-align: top;
}
:not(pre):not(.hljs) > code {
  color: #FA7A00; /* #F6AA00; #FF4B00; */
  padding: 1px 6px;
  /* background-color: #f8f8f8; */
  border: 1px solid #cccccc;
  border-radius: 3px;
}
code {
  color: #FA7A00;
  font-family: Consolas, Menlo, Meiryo, "Hiragino Kaku Gothic ProN", monospace;
  font-size: 1em;
}
pre {
  color: #FA7A00;
  background-color: transparent;
  border: 1px solid #cccccc;
  border-radius: 3px;
  overflow-x: auto;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.hljs {
  display: block;
  overflow-x: auto;
  color: #FA7A00;
  padding: 0.5em;
}
pre:not(.hljs), pre.hljs code > div {
    padding: 1px 16px;
    border-radius: 3px;
    overflow: auto;
}
a {
  color: #005AFF; /* #4DC4FF */
  text-decoration: underline;
}
a:visited {
  color: #003CAA; /* #002D80 */
}
a:hover {
  text-decoration: underline;
}
hr {
  border-top: 1px solid rgba(0, 0, 0, 0.35);
  border-bottom: 0;
  border-left: 0;
  border-right: 0;
}
table {
  text-indent: 0; margin: 0 0.5em;
}
td {
  border-top: 0;
  margin: 0 0.5em;
}
em {
  font-style: normal;
  -webkit-text-emphasis: filled dot;
}
strong {
  font-family: "BIZ UDGothic", sans-serif;
  font-style: normal;
  font-weight: 700;
}
.warning {
  padding: 3px 0;
  background: black;
  color: white;
  font-family: 'BIZ UDGothic', sans-serif;
  font-weight: 700;
}
/* ゴシック本文
body {
  font-family: "BIZ UDGothic", sans-serif;
  }
*/
/* 文字詰め 記入済み値は既定
p {
  margin-bottom: 1em;
}
h2 {
  font-size: 1.35em;
  margin-top: 24px;
  margin-bottom: 16px;
}
h3 {
  font-size: 1.25em;
  margin-top: 24px;
  margin-bottom: 16px;
}
body {
  line-height: 1.6;
}
*/
</style>
<!--
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS_CHTML"></script>
<script type="text/x-mathjax-config">
 MathJax.Hub.Config({
 tex2jax: {
 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
 }
 });
</script>
-->

# 

補足

Markdown PDF のヘッダ

Markdown PDF のヘッダについては,あまり自由度が高くない。

タイトルがファイル名になってしまうため非表示とした。設定からその都度手で打てばタイトルをヘッダに出すことはできるが,続かないと考えたのでそもそも消すことを選んだ。

日付は YYYY-MM-DD 形式で悪くないのだが,PDF が作られた日になってしまい,文書を配る日にできない。設定からその都度手で打てばタイトルをヘッダに出すことはできるが,続かないと考えたのでそもそも消すことを選んだ。

参考

Markdown PDF の導入

VSCode における Markdown PDF の設定

VSCode における Markdown のスニペット

Markdown における PDF 上の改行

HTML における em と strong

CSS の圏点

等幅フォント

その他

改訂

  • 改訂: 2025-11-16
    • 「スタイルシート」を更新した。
  • 改訂: 2025-05-20
    • 「スタイルシート」を更新した。フォントサイズを調整し,MathJax を既定ではコメントアウトすることにした。