电子书阅读器上的浏览器 [Day11] 移植 Firefox 阅读模式

这一篇的技术成份稍微高一点点。要谈到的功能,从一开始开发浏览器就有想要做,但是一直找不到比较好的实作方式。在经过两三周忙於其他的功能开发後,终於在这周找到比较恰当的切入点和相关技术的参考,得以完成心目中大致上的效果。

讲了一堆废话,究竟是什麽功能呢?

阅读模式!

不知道阅读模式的人,可以看一下下面的文章介绍。这功能几年前 Apple 在 Safari 浏览器中推出,让使用者可以更专心地阅读网页内容,不被广告和不必要的元件(标题栏,底部栏,侧边栏位等)干扰。

https://today.line.me/tw/v2/article/9PxxRK

之後,各家浏览器大厂也开始推出了类似的功能。 Brave 浏览器在推出他们自家的 SpeedReader 功能时,有顺便把市面上主要的实作都拿来做比较,有兴趣的人可以下载下面的 pdf 档来了解一下。主要比较了 Readability.js,Safari Reader View, Google Chrome DOM Distiller,BoilerPipe 和他们的 SpeedReader。

https://brave.com/wp-content/uploads/2020/08/speedreader-www19.pdf

原始的 Readability.js

对於这功能有了大致的了解後,要来决定一下怎麽开发这功能。第一个想法自然是找 Readability.js 来使用;一来它是 Open Source 的,二来,许多浏览器的阅读模式也是从它延伸而来的。用它的话,网路上可以找到的资源也会比较多一些。所以先上 Github 找了一个早期的版本来用。

https://github.com/Kerrick/readability-js

把 readability.js 放到 Android 的 assets 目录中,然後利用下面的方式载入档案,塞到目前的网页中。由於 readability.js 里头已经包含了初始化自己的程序码,而且会把处理过的内容,直接盖掉目前的网页内容,所以只要载入它就等着它把画面换成比较单纯的显示模式。

https://ithelp.ithome.com.tw/upload/images/20210907/20140260PCb9TyjoXI.png

原始的网页 套用 Readability.js 的网页
https://ithelp.ithome.com.tw/upload/images/20210907/20140260E2Q3fVuBBm.png https://ithelp.ithome.com.tw/upload/images/20210907/20140260Whn5T8wE5y.png

这方式实作上虽然很简单,但是产生出来的效果却不是很好。第一点是,很多应该不属於主要内容的部分,还是留在画面中;第二点是图片的部分,通常会过大。

字型忽大忽小尚可忽略不去计较,但是该小一点的图,大得吓人;和一堆不必要的元素依然存在,这效果很难让人有想阅读下去的念头。

原始的 readability.js 再加上 readability.css

仔细再研究了一下 readability.js 的内容,它在处理 html elements 时,除了会删除不必要的元素之外,也会把想要留下的元素加上特定的 class 或 id;然後它另外还有一个 readability.css 档案,应该就是用来规范这些新加的 class 是要怎麽呈现在画面中的。难怪只有执行 readability.js 的话,跑出来的画面有点惨不忍睹。

於是接下来的版本,我把 readability.css 也加进去。(也顺便把 WebView 从 Java 重构成 Kotlin 档案,不然改起来有点痛苦)

public void applyReaderMode() {
    InputStream jsInput, cssInput;
    try {
        jsInput = getContext().getAssets().open("readability.js");
        byte[] buffer = new byte[jsInput.available()];
        jsInput.read(buffer);
        jsInput.close();

        cssInput = getContext().getAssets().open("readability.css");
        byte[] cssBuffer = new byte[cssInput.available()];
        cssInput.read(cssBuffer);
        cssInput.close();

        // String-ify the script byte-array using BASE64 encoding !!!
        String encodedJs = Base64.encodeToString(buffer, Base64.NO_WRAP);
        String encodedCss = Base64.encodeToString(cssBuffer, Base64.NO_WRAP);
        loadUrl("javascript:(function() {" +
                "var parent = document.getElementsByTagName('head').item(0);" +
                "var script = document.createElement('script');" +
                "script.type = 'text/javascript';" +
                "script.innerHTML = window.atob('" + encodedJs + "');" +
                "parent.appendChild(script)" +
                "})()");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

套了之後,效果也没有因此而改善。所以我又试着去找其他的可能方案。

readerview feature from Mozilla Mobile

在开发的过程中,我主要拿来比较阅读模式效果的 reference app 分别是 Brave Browser 和 Firefox。Brave 在前面的 pdf 中有提到,它们的作法是在画面还没有真的绘制之前就可以先处理,速度会比其他的方案快;但缺点是,它的作法相对上也比较复杂,我不见得能够比照办理。
所以,我去找了 Firefox App的原始码来看(早该这麽做了)。原来 Mozilla 也有把它们的原始码放在 Github 上。而且针对 Readability 的改良版也特地独立成一个 repository 开发。

https://github.com/mozilla/readability

一开始我很开心地拿了这版本来套用。但又犯了一开始就犯的错误。javascript 只处理了资料的去留,但是真正呈现的部分还是需要对应的 css style file 来辅助才行。於是我找到了 Firefox Mobile App 的 repository,也找到了它 reader view 真正实作的地方。

https://github.com/mozilla-mobile/android-components/tree/master/components/feature/readerview

android-components/components/feature/readerview/src/main/assets/extensions/readerview/ 目录下,除了有上述的 readability.js 外,它又包了一层 readerview.js 和辅助的 readerview.css 。这两者的实作才是真正发挥 Readability 威力的地方。有兴趣的人可以进去看一下。大概说就是:readability 把资料处理完变成 article object 後, readerver.js 会拿 article 中的每个资料栏位,一个个贴上特定的 class name,然後在 readerview.css 中,针对这些 class 加上 UI 的呈现方式。

因为 readerview.js 中有很多是跟 firefox App 互动的实作,我不行整个档案直接拿来套用,所以我是抽取里头我需要的程序码来用而已。抽出来的程序码都在这儿:

https://github.com/plateaukao/browser/blob/my_version/app/src/main/assets/MozReadability.js#L2261

於是,跟 Firefox App 效果几乎一样的阅读模式完成了!(还有预估阅读时间要多久,不过这数值感觉不是很准确)

原本的网页 套用後的阅读模式
https://ithelp.ithome.com.tw/upload/images/20210907/20140260wGT6JzsZWd.jpg https://ithelp.ithome.com.tw/upload/images/20210907/20140260reheggJ017.jpg

看到右边的画面时,有没有一种很感动的感觉。画面中的 header, footer,广告等会吸引眼球注意力的元件都被去除了,只留下中间的内容部分。在一个自制的小 App 中能实作这样子的功能真的很令人感动。

参考原始码版本: https://github.com/plateaukao/browser/releases/tag/v8.5.2


<<:  每个人都该学的30个Python技巧|技巧 11:回圈二部曲—while回圈(字幕、衬乐、练习)

>>:  Day 11 : 用於生产的机械学习 - Data Labeling 资料标注

Day31:HTML(29) form(8)

输入类型"周" 将”input type="week"“允许...

冒险村10 - or equals

10 - or equals Ruby 的 ||=(or equals)运算符号使用频率是个使用频率...

高档爆大量,请提高警觉

在入场,且获利後,第一件事就是要保护获利,而不是不要赔钱。 通常有几个转折点需要注意,其中高档爆大量...

[DAY29]Line查询Postegre

记录完後我们就要开始查询了以下是蛮简单的查询方式。 import psycopg2 import o...

来一道色香味俱全的 JavaScript 吧

终於 进入好想工作室第 131 天 终於 我也迎来了传说中的 it 铁人赛 思考了很久铁人赛的主题要...