調べて分かる道具箱

Markdown を HTML にする

貼り付けた文章は、どこにも送りません。変換はぜんぶこの画面の中(お使いの機器の中)で行っています。

どの流儀で変換するか同じ文章でも、流儀が違えば違う HTML になります

GFM(GitHub Flavored Markdown)(版 0.29-gfm・2019年4月6日)で変換します。CommonMark に表・打ち消し線などを足したもの。

1回の改行をどうするか仕様と、GitHub のコメント欄とで食い違うところです

GFM(GitHub Flavored Markdown)の仕様 0.29-gfm(2019年4月6日)の範囲で変換しました。1回の改行では改行しない(仕様どおり)。

この変換で起きたこと

貼られた文章に、そのまま HTML にすると危ないものが入っていました。 タグの代わりに文字として扱っています。黙って消してもいません。何をどうしたかを下に全部書いています。

安全のために外したもの押すと文章ではなく命令が動くものがありました。文字はそのまま残しています。

危ない仕組みのリンクを外しました
リンク先が javascript: vbscript: data: file: のどれかで始まっていました。押すと文章ではなく命令が動くことがあるので、リンクをやめて文字だけ残しました。GitHub が使っている変換器(cmark-gfm)も、この4つを既定で外しています。
見つかったもの: javascript:alert(1)

タグとして働かせず、文字にしたもの生の HTML です。消してはいません。そのままの文字として出しています。

生の HTML(2か所)
<div> や <script> のようなタグは、タグとして働かせず、そのままの文字として出します。貼り付けた文章に他人が書いた <script> が入っていることがあるためです。
見つかったもの: <script>
<script> が入っていました
貼り付けた文章に <script> が入っていました。この道具は生の HTML をタグとして働かせないので、そのままの文字として出しています。動くことはありません。なお GitHub が使っている変換器も script を含む9つのタグだけは字に直しますが、そのほかの HTML は通します。
見つかったもの: <script>
見た目(プレビュー)貼った文字をタグとして解釈せず、変換した木からこの画面を組み立てています

川の馬

カバは 鼻の穴・目・耳 が顔の側面で一直線に並びます。 だから水に潜ったまま、呼吸と警戒ができます。

名前のもと

  • ἵππος(híppos)… 馬
  • ποταμός(potamós)… 川
    • つなげて「川の馬」
  1. 水の中で過ごす
  2. 夜に草を食べる

カバは泳ぐのではなく、川底を 歩いて います。

部位高さ
鼻の穴水面より上
口水面より下
const kaba = "ポタモ";

ポタモのトップへ と <script>alert(1)</script> と 危ないリンク を入れてあります。

書き出す HTMLプレビューと同じ木から作っているので、食い違いません
<h1>川の馬</h1>
<p>カバは <strong>鼻の穴・目・耳</strong> が顔の側面で一直線に並びます。
だから水に潜ったまま、呼吸と警戒ができます。</p>
<h2>名前のもと</h2>
<ul>
<li>ἵππος(híppos)… 馬</li>
<li>ποταμός(potamós)… 川
<ul>
<li>つなげて「川の馬」</li>
</ul></li>
</ul>
<ol>
<li>水の中で過ごす</li>
<li>夜に草を食べる</li>
</ol>
<blockquote>
<p>カバは泳ぐのではなく、川底を <em>歩いて</em> います。</p>
</blockquote>
<table>
<thead>
<tr>
<th>部位</th>
<th align="right">高さ</th>
</tr>
</thead>
<tbody>
<tr>
<td>鼻の穴</td>
<td align="right">水面より上</td>
</tr>
<tr>
<td>口</td>
<td align="right">水面より下</td>
</tr>
</tbody>
</table>
<pre><code class="language-js">const kaba = "ポタモ";
</code></pre>
<p><a href="/">ポタモのトップへ</a> と &lt;script&gt;alert(1)&lt;/script&gt; と
危ないリンク を入れてあります。</p>

670文字。貼り付けられるのは200,000字までです(超えたら、切り捨てずにお断りします)。

よくある質問

Markdown はひとつの決まりではないのですか?

違います。2004年に作られたもとの Markdown には、書き方をはっきり決めた文書がありませんでした。そのため道具ごとに解釈が分かれ、同じ文章が別の HTML になります。あとから食い違いを無くすために書かれたのが CommonMark で、それに表などを足したのが GitHub の GFM です。この道具は、どちらで変換したかを画面に必ず出します。

1回だけ改行したのに、くっついて1行になるのはなぜですか?

仕様どおりの動きです。CommonMark も GFM も、1回の改行は「やわらかい改行」として扱い、HTML の中では改行のまま残しますが、ブラウザはそれを行の区切りとして描きません。改行したいときは行末に空白を2つ置くか、\ を1つ置きます。空行で区切れば別の段落になります。GitHub でも、.md ファイルではくっつき、issue やコメント欄では改行されます。

同じ GitHub なのに、ファイルとコメント欄で結果が違うのはなぜですか?

コメント欄だけ「1回の改行を改行として扱う」設定になっているためです。GitHub 自身の説明にも、.md ファイルでは1行になるので空白2つか \ か <br> が要る、と書かれています。仕様(GFM 0.29-gfm)にこの決まりはありません。同じ文章を README に貼ったときだけ形が崩れるのは、たいていこれが理由です。

貼り付けた文章はどこかに送られますか?

送られません。変換はすべてこの画面の中(お使いの機器の中)で行っています。変換の道具を外から読み込むこともしていません。さらに、文章の中に画像があってもプレビューでは読み込みません。読み込むと、その画像の置き場に「この文章を見た人がいる」と伝わってしまうためです。

貼った文章に <script> が入っていたらどうなりますか?

動きません。この道具は、貼られた文字をタグとして解釈しない作りにしています。<script> はそのままの文字として画面と HTML に出て、「タグとして働かせず文字にしました」と画面にも出します。押すと命令が動く javascript: などのリンクは外しますが、そのときも文字は残し、外したことを画面に書きます。

対応していない書き方を貼るとどうなりますか?

「対応していません」と画面に出します。脚注([^1])や参照の形のリンク、絵文字の合言葉(:smile:)などがそれにあたります。そのままの文字として出したうえで、何が対応していないのかを名前で示します。黙って素通しすると、変換されたつもりで壊れた HTML を持ち帰ることになるためです。

貼り付けると、見た目とHTMLの両方を出します。どの流儀で変換したかを必ず画面に書きます。

「Markdown」は、ひとつの決まりではありません

ここがこのページで一番大事なところです。同じ文章でも、変換する道具によって違う HTML が出てきます。道具の出来の良し悪しではなく、そもそも決まりが何種類もあるからです。

はじまりは2004年です。ジョン・グルーバーという人が Markdown を作りました。 配布されている最後の版は1.0.1・2004年12月17日で、いまも同じものが置かれています。 ところがこの Markdown には、書き方をすみずみまで決めた文書がありませんでした。 説明の文章と、変換する道具(Perl で書かれたもの)があるだけです。

書いていないところは、道具を作る人がそれぞれ決めるしかありません。 こうして解釈が枝分かれしていきました。それを整理するために書かれたのがCommonMarkで、いまの版は0.31.2(2024年1月28日)です。 CommonMark 自身がその理由を「あいまいでない仕様が無いため、実装はこの10年でかなり食い違ってしまった」(新しいタブで開きます)と書いています。

その CommonMark に、表・打ち消し線・チェックつきの箇条書きなどを足したのがGFM(GitHub Flavored Markdown)です。仕様書は0.29-gfm(2019年4月6日)。 もとの Markdown(2004年)に表はありません。いま当たり前のように書いている表は、あとから足されたものです。

枝分かれの多さは、インターネットの決まりごとを書く文書にも表れています。RFC 7763(新しいタブで開きます)(2016年3月)は Markdown を送るときの型を決めたものですが、そこには「Markdown はいまや、人には広く通じるが、たがいには必ずしも通じない書き方の一族を指す」と書かれ、どの流儀かを添えて送る決まりになっています。 決まりが1つなら、こんな仕組みは要りませんでした。

だからこの道具は、変換した結果のすぐ上に「どの流儀の、どの版で、改行をどう扱って変換したか」を必ず出します。 流儀を書かない変換結果は、あとで別の場所に貼ったときに形が変わっても、 なぜ変わったのかを追いかけられません。

⭐ 同じ3行が、決まりしだいで違う HTML になります

いちばん食い違うのが改行です。空行を入れずに改行だけで分けた3行を、 決まりだけ変えて2通りに変換してみます。元の文章はまったく同じです。

元の文章(3行)空行を入れずに、改行だけで3行に分けたもの
春はあけぼの
やうやう白くなりゆく
山ぎは
① 1回の改行では改行しない(CommonMark・GFM の仕様どおり)GitHub の .md ファイル、Jekyll、Pandoc などがこちら
<p>春はあけぼの
やうやう白くなりゆく
山ぎは</p>

春はあけぼの やうやう白くなりゆく 山ぎは

② 1回の改行を <br> にするGitHub の issue やコメント欄、Slack、多くのチャットがこちら
<p>春はあけぼの<br />
やうやう白くなりゆく<br />
山ぎは</p>

春はあけぼの
やうやう白くなりゆく
山ぎは

①が仕様どおりです。CommonMark も GFM も、1回の改行を「やわらかい改行」と呼び、HTML の中では改行の文字のまま残すが、行の区切りにはしないと決めています。仕様には「やわらかい改行を、強い改行として描く選び方を用意してもよい」(新しいタブで開きます)とも書かれていて、②はそちらです。どちらも仕様の中にあるので、どちらが正しいという話ではありません。

もとの Markdown を作ったグルーバー自身が、その理由を書き残しています。「すべての改行を br にする単純な決まりは Markdown ではうまくいかない。 メール風の引用や、段落がいくつもあるリストの項目は、 手で折り返して書いたほうが読みやすいからだ」。 つまり「書いている文章そのものが読みやすいこと」を優先した結果、 改行が1つでは足りない決まりになったわけです。

同じ GitHub でも、ファイルとコメント欄で結果が違います

ここが実際にいちばん引っかかるところです。GitHub 自身の説明に、issue やコメント欄では改行がそのまま改行になるけれど、.md ファイルでは1行につながってしまうので、 空白2つか \ か <br> を書き足す必要がある、 と書かれています。

GFM の仕様書(新しいタブで開きます)のほうには、この「1回の改行で改行する」という決まりはありません。つまりコメント欄の動きは仕様ではなく、GitHub の画面の設定です。 コメント欄で書いた文章をそのまま README に貼ると形が崩れるのは、たいていこれが理由です。

日本語だと、つないだところに空白が入りません

①をよく見てください。2行が空白なしでつながっています。これは変換のしかたではなく、ブラウザが描くときの決まりです。

HTML の中では改行はそのまま残っていて、ブラウザがそれを空白に変えるか、消すかを決めます。 英語のように語と語を空白で区切る言語では、消してしまうと「isbroken」のように単語がくっついてしまうので空白に変えます。 日本語や中国語には語の区切りの空白が無いので、消すのが正しいわけです。

CSS の仕様(CSS Text Module Level 3(新しいタブで開きます))は、この判断を「前後の文字を見て決める。やり方はブラウザに任せる」と書いています。さらに「昔の HTML と CSS は改行を必ず空白に変えていて、そのせいで中国語などが行を折って書けなかった」という注記まで付いています。 改行を消してよくなったのは、比較的あたらしい話です。

貼った文章は、どこにも送りません

変換はすべてこの画面の中(お使いの機器の中)で行っています。 文章が機器から出ることはないので、社外に出せない資料でもそのまま貼れます。変換の道具を外から読み込むこともしていません。よく使われる変換の道具を配布元から読み込む作りにすると、その配布元に「誰かがこのページを開いた」ことが伝わります。 だから対応する書き方を絞って、自分で書いています。

同じ理由で、プレビューでは画像を読み込みません。 文章の中に ![説明](絵の場所) があったとき、そのまま画像として出すと、 ブラウザがその置き場へ絵を取りに行きます。置き場の持ち主に「この文章を見た人がいる」と伝わってしまうので、 枠と説明の文字だけを出しています。書き出す HTML のほうには、ちゃんと画像として書きます。

他人の書いた文章には、危ないものが入っていることがあります

Markdown を貼る場面は、他人が書いたものを見るときが案外多いものです。 そこには<script>や、押すと命令が動くリンクが混ざっていることがあります。

この道具は、変換した結果をいったん「木」の形にしてから画面を組み立てます。 貼られた文字は必ず「文字の場所」にしか入らないので、 タグとして働く道がそもそもありません。 <script> はそのままの文字として出て、「タグとして働かせず、文字にしました」と画面に出します。 黙って消しません。消してしまうと、もとの文章に何が書いてあったのかが分からなくなるからです。

リンクは別です。javascript: vbscript: data: file: で始まるものは、 押すと文章ではなく命令が動くのでリンクをやめて文字だけ残します。 この4つは思いつきで選んだものではなく、GitHub が使っている変換器(cmark-gfm)(新しいタブで開きます)が、既定で外すものとして挙げている4つと同じです。

【へえ】GFM の仕様には安全のための決まりもありますが、ごくわずかです。「使わせない生の HTML」という節にあるのはtitle・textarea・style・xmp・iframe・noembed・noframes・script・plaintext の9つだけで、しかも「そのほかの HTML タグはそのまま通す」とはっきり書かれています。 つまりGFM の決まりを守るだけでは安全になりません。 cmark-gfm の説明にも「生の HTML を通す設定にするなら、 別に専用の掃除の道具を使うことをすすめる」と書かれています。 この道具が生の HTML を1つも通さないのは、そのためです。

対応している書き方の一覧

外から道具を取ってこない代わりに、対応する書き方を絞っています。 ここに無いものを貼ったときは、そのままの文字として出したうえで「対応していません」と画面に出します。 黙って素通しすると、変換されたつもりで壊れた HTML を持ち帰ることになるからです。

この道具が変換する書き方
書き方覚え書き
見出し# 見出し6段まで。下に === や --- を引く書き方も
段落ふつうの文章空行で区切ります
強い強調**太字**__太字__ も同じ
強調*斜体*_斜体_ も同じ
打ち消し線~~消す~~GFM だけ。そのまま変換します
コード(1語)`code`中の記号はそのまま出します
コード(かたまり)```js空白4つで字下げする書き方も
リンク[名前](https://例)危ない仕組みのものは外します
画像![説明](絵.png)説明の文字は必ず残します
箇条書き- 項目* と + も。入れ子にもできます
番号つき1. 項目1) も。始まりの番号を引き継ぎます
引用> 引用中に見出しやリストも書けます
区切り線---*** と ___ も
表| 名前 | 数 |GFM だけ。:--- で寄せる向きを決めます
行の中の改行行末に空白2つ行末の \ でも同じ
記号を字として出す\*星\*前に \ を置きます

強調(* と _)の判定は、仕様のこまかい決まりをそのまま写したものではありません。 ただし「_ は語の途中では強調にしない」だけは入れてあります。 これが無いと snake_case_name の真ん中が斜体になってしまい、 プログラムの名前を書いた文章が読めなくなるためです。