1. 程式人生 > >Android使用marked.js渲染markdown文件

Android使用marked.js渲染markdown文件

Android使用marked.js渲染markdown文件

一、目標

通過瀏覽器渲染markdown文件,實現預覽功能

1. 效果圖

在這裡插入圖片描述

  • 左側——Smartisan瀏覽器
  • 右側——UC瀏覽器

2. 下載地址

神馬筆記最新版本:【

神馬筆記 版本1.2.0.apk

二、功能設計

神馬筆記支援圖文混排的筆記形式,但圖片與文字格式文件無法滿足圖文混排的方式。

因此,需要第三種形式的文件格式,並且支援圖文混排。

markdown、html、pdf、word、……

所有備選格式中,markdown是最為簡單,並且匯出後的格式也可以二次編輯,實在是最理想的第三種文件格式。

雖然神馬筆記的筆記格式很容易轉換為Markdown格式。但是?

神馬筆記目前是不支援編輯markdown格式,也沒有預覽markdown格式的功能,並且Android系統也沒有內建Markdown格式的閱讀器。那麼怎麼才能預覽匯出的markdown文件了?

將Markdown文件轉化為html,再呼叫瀏覽器進行預覽!

Markdown轉Html用2中實現方式

  1. 使用Markdown解析器,直接輸出為Html語法文件;
  2. 在Html使用JS解析器,動態解析Markdown內容;

這裡,我們使用的是第二種方式,將所有工作交於瀏覽器來實現。

三、準備工作

首先,需要JavaScript版本的markdown的解析器。這裡選擇了marked.js。

官方網站:https://marked.js.org

GitHub專案地址:https://github.com/markedjs/marked

其次,需要CSS樣式用來渲染。這裡使用了GitHub風格樣式github-markdown-css。

GitHub專案地址:https://github.com/sindresorhus/github-markdown-css

最後,處理程式碼高亮。

雖然神馬筆記暫時還未支援編輯程式碼,但Markdown是支援程式碼格式的。

因此,這裡選擇highlight.js實現程式碼高亮。

官方網站:https://highlightjs.org/

GitHub專案地址:https://github.com/highlightjs/highlight.js

最後的最後,選擇CDN伺服器用來載入JS指令碼及CSS樣式,選擇以下2個CDN網站。

BootCDN:https://www.bootcdn.cn/

CDNJS:https://cdnjs.com/

四、組合起來

1. template.html

template.html載入了外部JS指令碼及CSS樣式

並且提供了2個模版引數

  • {title} 標題
  • {markdown} markdown內容
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8"/>

    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <link href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.10.0/github-markdown.min.css"
          rel="stylesheet"/>
    <style>
        .markdown-body
        {
            box-sizing: border-box;
            min-width: 200px;
            max-width: 980px;
            margin: 0 auto;
            padding: 45px;
        }

        @media (max-width: 767px)
        {
            .markdown-body
            {
                padding: 15px;
            }
        }
    </style>

    <link href="https://cdn.bootcss.com/highlight.js/9.13.1/styles/default.min.css" rel="stylesheet"/>

    <script src="https://cdn.bootcss.com/highlight.js/9.13.1/highlight.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/0.6.0/marked.min.js"></script>

    <script>

		marked.setOptions({
		    renderer: new marked.Renderer(),
		    gfm: true,
		    tables: true,
		    breaks: true,
		    pedantic: false,
		    sanitize: false,
		    smartLists: true,
		    smartypants: false,
		    highlight: function (code, lang) {
		          if (lang && hljs.getLanguage(lang)) {
		            return hljs.highlight(lang, code, true).value;
		          } else {
		            return hljs.highlightAuto(code).value;
		          }
		      }
		});

    </script>

    <title>{title}</title>
</head>

<body>
<article id="content" class="markdown-body" style="text-align: left;"></article>

<script>
  	var text = '```java\npublic class BitmapUtils {}\nString text = null; \n```';

  	text = '{markdown}';
  	text = marked(text);

    document.getElementById('content').innerHTML = text;
</script>
</body>

</html>

2. escape

替換{markdown}模版引數時,必須進行轉義,否則無法正常顯示。

借用了JSONStringer的一段程式碼實現轉義。

static String escape(String s) {

    StringBuilder out = new StringBuilder(s.length() + 128);

    for (int i = 0, length = s.length(); i < length; i++) {
        char c = s.charAt(i);

        /*
             * From RFC 4627, "All Unicode characters may be placed within the
             * quotation marks except for the characters that must be escaped:
             * quotation mark, reverse solidus, and the control characters
             * (U+0000 through U+001F)."
             */
        switch (c) {
            case '"':
            case '\\':
            case '/':
                out.append('\\').append(c);
                break;

            case '\t':
                out.append("\\t");
                break;

            case '\b':
                out.append("\\b");
                break;

            case '\n':
                out.append("\\n");
                break;

            case '\r':
                out.append("\\r");
                break;

            case '\f':
                out.append("\\f");
                break;

            default:
                if (c <= 0x1F) {
                    out.append(String.format("\\u%04x", (int) c));
                } else {
                    out.append(c);
                }
                break;
        }

    }

    return out.toString();
}

五、Final

完成所有工作,匯出Markdown檔案,匯出相關的圖片,匯出預覽的Html檔案。

呼叫第三方應用檢視預覽的html檔案,你會驚喜的發現圖片載入不出來

檢查程式碼,發現一切都沒有問題後,仍會發現圖片還是載入不出來

主要原因在於:

Android 7.0(API 24)之後,呼叫Intent不能傳遞file://形式的Uri,只能通過content://傳遞本地檔案Uri。

如此一來,瀏覽器無法定位相對路徑。

即使檔案中的圖片路徑使用絕對路徑,採用file://路徑形式,依然無法載入圖片。

以下是一些測試結果,僅供參考。

機型 呼叫位置 瀏覽器 顯示結果
堅果Pro2 神馬筆記 Smartisan瀏覽器 無法顯示
Chrome 無法顯示
UC瀏覽器 正常顯示
Smartisan檔案管理器 Smartisan瀏覽器 正常顯示
Chrome 不支援
UC瀏覽器 正常顯示

檢視瀏覽器位址列,發現Smartisan檔案管理器傳遞的Uri為file://

機型 呼叫位置 瀏覽器 顯示結果
紅米6 Pro 神馬筆記 MIUI瀏覽器 正常顯示
Chrome 無法顯示
UC瀏覽器 正常顯示
MIUI檔案管理器 MIUI瀏覽器 正常顯示
Chrome 無法顯示
UC瀏覽器 正常顯示

檢視位址列地址,發現MIUI檔案管理器傳遞的Uri為**file://content://**兩種形式。

MIU瀏覽器和UC瀏覽器顯示為file://

Chrome顯示為content://

結論:

  • UC瀏覽器完美支援file://及content://
  • MIUI瀏覽器完美支援file://及content://
  • Smartisan瀏覽器支援file://
  • Chrome無法顯示本地圖片

~奈何~奈何~