R Notebookの "Download Rmd"で日本語を扱えるようにする

# :congratulations: 公式に修正されました（2018-05-17 加筆）

この問題については、2017-11-11 リリースの **rmarkdown 1.7** で `fix #722` として修正されていました。アプローチは自分と違っていましたが、rmarkdown 1.9を使ったR Notebookで問題なく日本語（utf-8）を含む.Rmdを取り出せることを確認しました。

* https://github.com/rstudio/rmarkdown/releases/tag/v1.7
* [fix #722: use &lt;a download href="base64"&gt; instead of FileSaver.js to …](https://github.com/rstudio/rmarkdown/commit/c424869dc71f9000c73b54c935704abb53891614)

**このページの workaround は、rmarkdown 1.7 以降ではもはや正常な挙動を上書きしてしまうので使用しないでください。**

# TL; DR

* R Notebookでは、作成した.nb.htmlドキュメントの`Code`ボタン - `Download Rmd`から元の.Rmdがダウンロード可能
* しかし日本語（非ASCII文字？）が含まれると、ダウンロードしたコードが文字化けしてしまう
* 回避するには、**rmarkdown** パッケージによる `window.initializeSourceEmbed()`関数を変更すればよい
* 毎回設定するのは大変なので、YAMLフロントマターで別ファイルを読み込むのが楽

# はじめに

この記事は、昨年（2016年）末にSlackのr-wakalangで教えて頂いた方法の備忘録です。
Slackの方で記事が流れて見れなくなってしまったので、こちらにまとめ直しました。

## R Notebookとは

[R Notebook](http://rmarkdown.rstudio.com/r_notebooks.html)は、R Markdownを利用した文書をインタラクティブに作成できる仕組みです。昨年（2016年）夏頃に **RStudio + rmarkdown** パッケージの環境に導入されました。
最近はJupyter notebookの方が人気のようで、R Notebookが使えるようになった直後のように話題になっていない感もありますが、個人的にはR Markdownで書いてknit HTMLをしながら進めるより作業しやすいので重宝しています。

出力は .nb.html (R Notebook HTML Format) で、これ自体に元の.Rmdが埋め込まれたHTMLになっています。そのため、.nb.html だけを別の環境に移動しても、RStudioで読み込めば元のコードを復元して作業を継続することが可能です。

## 問題点

出力した .nb.html のページトップ右側に「Code▼」のボタンがあり、ページ全体のコードの表示・非表示の切り替えができるとともに、「Download Rmd」で埋め込まれている元の.Rmdを取り出すことができます。

...が、元の.Rmdに日本語（おそらく他の非ASCII文字でも）が含まれるとダウンロードした.Rmdは文字化けしてしまい、再利用ができません。

参考：https://kazutan.github.io/TokyoR55/Rnotebook_intro.html

# 回避策

.nb.htmlのソースを見てみると、末尾近くに`<div id="rmd-source-code">...</div>`という要素があり、BASE64エンコードされた元の.Rmd文書が埋め込まれています。コピーして`window.atob()`に通してみると、見覚えのある文字化けしたコードになりました。
関連する部分について調査すると、Javascriptの`window.atob()`関数とユニコードの間には問題があることが分かりました。回避法も確立しているようです。

参考：https://developer.mozilla.org/ja/docs/Web/API/WindowBase64/btoa#Unicode_Strings

そこで、Chrome開発者ツールで`#rmd-source-code`を扱っている部分を探すと、`window.initializeSourceEmbed()`という関数の中に問題の部分がありました。この関数は、rmarkdownパッケージにより作成されている関数（GitHub rstudio/rmarkdownの[該当ページ](https://github.com/rstudio/rmarkdown/blob/7de1558434c5880e70a93b0e4a228b61c92e211d/inst/rmd/h/navigation-1.1/sourceembed.js)）で、DOM readyのタイミングで呼び出されています。

```js:original
window.initializeSourceEmbed = function(filename) {
  $("#rmd-download-source").click(function() {
    var src = window.atob($("#rmd-source-code").html());
    var blob = new Blob([src], {type: "text/x-r-markdown"});
    saveAs(blob, filename);
  });
};
```

これを、

```js:modified
window.initializeSourceEmbed = function(filename) {
  $("#rmd-download-source").click(function() {
    var src = window.atob($("#rmd-source-code").html());
    
    // add this line
    src = decodeURIComponent(escape(src));
    
    var blob = new Blob([src], {type: "text/x-r-markdown"});
    saveAs(blob, filename);
  });
};
```

に書き換えれば良いと考え、.Rmdの先頭で`<script>`要素を作って挿入することで上書きしたところ、「Download Rmd」で取り出した.Rmdファイルの文字化けは解決しました。

上記をr-wakalangのrmarkdownチャンネルで報告し、より扱いやすい方法について質問したところ、@kazutanさんにYAMLフロントマターを使う方法を教えて頂きました。

# 結局どうするか

まず、上記の`window.initializeSourceEmbed()`を上書きするコードを外部HTMLファイルとして用意します。

```html:for_notebook_header.html
<!-- Download Rmd で日本語を含む元ファイルをダウンロード出来るようにする -->
<script>
  // overwrite R Notebook's original code
  window.initializeSourceEmbed = function(filename) {
    $("#rmd-download-source").click(function() {
      var src = window.atob($("#rmd-source-code").html());
      src = decodeURIComponent(escape(src));
      var blob = new Blob([src], {type: "text/x-r-markdown"});
      saveAs(blob, filename);
    });
  };
</script>
```

そして、そのファイル（for_notebook_header.html）を.RmdのYAMLフロントマターで読み込めば完了です。

```yaml:sample.Rmd
---
title: "R Notebook sample"
author: "@mokztk"
output:
  html_notebook:
    includes:
      in_header: for_notebook_header.html
---
```

根本的にはGitHubにissueかpull requestを立てるのが筋なのですが、自分にはまだハードルが高いので上記で運用しています。
お困りの方の参考になりましたら幸いです。