公式に修正されました(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 <a download href="base64"> instead of FileSaver.js to …
このページの 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は、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の該当ページ)で、DOM readyのタイミングで呼び出されています。
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);
});
};
これを、
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ファイルとして用意します。
<!-- 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フロントマターで読み込めば完了です。
---
title: "R Notebook sample"
author: "@mokztk"
output:
html_notebook:
includes:
in_header: for_notebook_header.html
---
根本的にはGitHubにissueかpull requestを立てるのが筋なのですが、自分にはまだハードルが高いので上記で運用しています。
お困りの方の参考になりましたら幸いです。