譯文|魔改二維碼

Matrix 是少數派的寫作社群,我們主張分享真實的產品體驗,有實用價值的經驗與思考。我們會不定期挑選 Matrix 最優質的文章,展示來自使用者的最真實的體驗和觀點。

文章代表作者個人觀點,少數派僅對標題和排版略作修改。

讓我們看看如何在不讓連結失效的前提下隨心所欲地更改二維碼的外觀。我們還會展示世界上第一個(據我所知)可以正常使用的動態二維碼。

來 My-QR。art 看看效果怎麼樣吧!

二維碼是將現實世界和數字世界連通的一種簡單可行的方式。每當我看到一個二維碼,我都會想掃一掃,看看它後面隱藏了什麼東西,二維碼就是這樣讓我著迷。但是二維碼看起來總是千篇一律,有沒有什麼辦法能讓他們看起來更炫酷一點呢?比如說,能不能把二維碼做成某個特定的形狀,或者做成畫素畫的樣子呢?

人們已經利用了(或者說是濫用了)二維碼的糾錯功能來在二維碼中新增他們的 logo:他們只需把自己的 logo 放在二維碼的一部分上,並讓糾錯功能處理丟失的資料就好了。然而,如果你想用這種方式來修改二維碼,你最多隻能遮擋 30%(實際來講通常會比這個數值小很多)的二維碼面積。換個思路想的話,既然二維碼本質上就是資料的一種視覺化形式,我們能不能換一種角度來看這個問題呢?比如說,我們不去思考如何設計二維碼來讓它適合我們的資料,而是讓資料適合我們的二維碼,這樣可不可以呢?

先上結論:完全可以。看看 My-QR。art 就知道了。所有的程式碼都完全公開,當然也歡迎 來 GitHub 提交程式碼。

思路

比如說,我們想要給我們的網站做一個推廣。而且,我們現在正好有一個我們網站的黑白 logo。我們要做的就是去做一個引人注意的二維碼,讓它長得像我們網站的 logo。

當然,二維碼所指向的網址我們也有,比如說這個:https://my-qr。art。

我們已經設定好了一臺伺服器,用來重定向特定的連結。這樣之後,我們可以把二維碼頭部的資料設定成指向那臺伺服器的連結,於是二維碼後面所表示的資料就可以由我們自己來定義了。因此,我們就可以自由設計這部分資料,來讓二維碼符合我們的設計了!

舉個例子吧:二維碼所指向的網址是 https://my-qr。art/r/是什麼都無所謂的一段超長字串 ,在伺服器後端我們可以告訴伺服器,將任何指向 https://my-qr。art/r/是什麼都無所謂的一段超長字串 這個網址的請求都重定向到 https://my-qr。art 這裡。於是,我們這就做好了一個能夠正常使用的二維碼:它既可以將我們帶往指定的網址,同時又能對二維碼的外觀進行更多的自定義!多達 80% 的程式碼都可以用這種方式來編輯。

這些都搞定之後,就算是想做動態二維碼也不在話下。我們可以獨立製作每一幀,並讓它們都重定向到同一個頁面上。確實,打印出來的二維碼佔多數,所以動態二維碼可能沒什麼意義,但它們看起來真的很酷!為了讓你在接下來的閱讀中不至於昏昏欲睡,先給你看一個世界上(據我所知)第一個可以正常工作的動態二維碼:

奔跑的馬兒有沒有讓你想起某部電影……?

二維碼的工作原理是怎樣的?

我們既然知道了思路,那就開始著手做吧。但首先,我們要弄清楚二維碼的工作原理是什麼樣的。

二維碼使其中的一部分方塊變黑,一部分方塊變白,以此來編碼資料。這之後,二維碼掃描器透過識別這些黑色和白色的小方塊就可以復原其資訊。二維碼的尺寸多種多樣,從版本 1(非常小,17×17)到版本 40(非常大,177 × 177),共有 40 種之多。二維碼中黑白的小方塊叫做「模組」,每一個模組都有以下這四種功能之一:

建立一個模板

在我們的計劃中,使用者可以決定所有實行功能 3 的模組的顏色。於是,第一點要做的就是讓使用者知道,在他們選定尺寸的二維碼中,哪些畫素使用者可以控制,哪些不可以。我們先建立一個模板,讓實行功能 3 的模組呈現白色,而其他模組全都呈現灰色。

這樣,我們就可以自行為其他模組和糾錯資料填充顏色了。為了做到這一步,你需要深入瞭解二維碼的工作原理。我們需要考慮資料編碼、資料交錯(interleaving)、額外的識別點(extra eyes)、糾錯資訊(error words)等等。同時,我們也要考慮到我們需要為我們的連結預留前面的幾個字元。

幸運的是,網際網路上有這麼一個很棒的資源:Thonky。com 提供的二維碼教程。在一個版本 30 的二維碼上處理了這些事情之後,我們就會得到這樣的一張圖片。

讀取設計

現在我們來解決另一個問題:在使用者在我們的模板上繪製了他自己的設計之後,我們要如何將其轉換成可用的二維碼?二維碼有四種可用的編碼型別:數字、字母數字、二進位制和漢字。我們需要用編碼來建立連結,所以數字和漢字這兩種是不行的。其次,我們需要使連結結尾的隨機字串足夠正常,這樣二維碼掃描器才會將我們的程式碼識別為一個連結。二進位制編碼方式通常會產生一些不會出現在連結中的字元組合(比如說「 」),這就會導致許多二維碼掃描器不會將其作為連結開啟。

幸運的是,字母數字編碼方式剛剛好有足夠的字元來讓我們建立可用的連結(儘管生成的字母只能是大寫的)。

弄明白這一點之後,我們來讀取使用者的設計吧。首先我們需要從設計中的所有資料模組中提取一個位串(bit string)。對於較小的二維碼來說這一過程相當直接;只需要像 Thonky 提供的上圖一樣,從上到下,從右到左以蛇形方式讀取即可。但是對於尺寸大於 5 的二維碼來說,我們就要考慮到資料交錯的問題了。在尺寸大於 5 的二維碼中,資料資訊散佈在二維碼的各處,也許這樣做是為了讓資料樣式更加隨機。所以,我們需要將這個交錯程式逆轉過來。

幸運的是,Thonky。com 對於這一點也同樣進行了解釋。最後我們從我們的設計中提取出一個長達 13863 位長的位串,現在我們只展示前 80 個字元:

給位串解碼

現在,我們需要將位串解碼,使其變成一個字母數字串。我們按照每 11 位一組的方式對位串進行分組,並且按照 這個表 來將其轉化成 2 個字母/數字字元。我們得到的結果就是下面這樣(同樣是部分):

KS$#LKAHRDF9H8XQI9AL HRD$OIWHSRE94QX95IFR*IK9HF/ 5NM+/AMIA99LB I5R II98ULR JRD/AR1 IRG8。。。

我們把前 20 位換成我們的連結字首,之後就會得到:

HTTPS://MY-QR。ART/R/ HRD$OIWHSRE94QX95IFR*IK9HF/ 5NM+/AMIA99LB I5R II98ULR JRD/AR1 IRG8。。。

生成二維碼

現在我們可以隨便用個二維碼庫來生成二維碼了!二維碼庫會幫我們處理糾錯碼以及其他無聊的部分。

但怎麼生成了這樣的東西?這看起來也不像影象呀?問題出在遮罩上。二維碼規格中有五種不同的遮罩,其中這種使我們的二維碼看起來最為隨機化的是選擇好的,為了讓二維碼掃描器更易閱讀。但這肯定不是我們想要的效果。但是,以我的經驗來說,掃描器掃描這些不隨機的資料也沒什麼問題,所以即便把二維碼庫設定為總使用同一種遮罩,我們也不必太擔心。我們先給我們的設計應用相同的遮罩,再進行處理,我們終於得到了我們想要的影象。

將請求重定向

現在我們要將二維碼重定向到正確的域名上。這按理來說很簡單:只要把二維碼的網址放在資料庫中,然後連結到重定向的網址就行了。當我們收到請求時,只需要在資料庫中查詢相應的網址即可。然而,如果我們想用現代的網路伺服器,我們就還得兜幾個圈子。Apache 不喜歡我們生成的大多數連結,因為它們看起來都不像正常的連結(這就不好玩了是不是,Apache?)。於是我們會收到一條錯誤資訊:Error 400: Bad request。而且,似乎我們還沒辦法把這條錯誤資訊關掉。

但幸運的是,有一個變通的辦法:我們可以為我們的 Error 400 頁面設定一個自定義的錯誤頁面,所以如果我們把重定向頁面設定為 Error 400 的錯誤頁面,就搞定了!幸虧 Apache 沒有掃興到在處理 Bad Request 時再給我們一個 Bad Request 錯誤……

我的後端用的是 Django,處理這些亂七八糟的連結時,它還會出現另一個令人哭笑不得的 bug:它會嘗試將連結作為一個 ISO-8859-1 的字串來解碼,但是 Apache 傳過來的字元中有些不在 ISO-8859-1 中。這時,我們就需要我們自己的 WSGI 處理程式來解決這個問題了:具體看這裡。但現在所有問題總算是解決了,掃一掃我們創作的二維碼,它成功地重定向到了我們想要的地方!

不過還是有一個小小的注意事項需要說明:二維碼本身是標準化的,我們建立的是標準的二維碼,其中包含正確的連結;但是二維碼掃描器未必是標準化的,也就是說有的可能會將我們的二維碼識別為文字,而不是連結。

人人都能設計自己的二維碼!

因為處理影象什麼的對使用者來說不怎麼友好,所以我用 Django 做了個網頁應用,在 My-QR。art 這裡。你可以使用這個易於上手的 編輯器 來畫、填充、或者上傳黑白圖片來建立你自己的二維碼。同時,它也有一些我們做的解釋和重定向程式碼。專案本身完全免費開源,可以來 GitHub 主頁 檢視詳細資訊。

寫在最後

終於可以依據我們的意願建立二維碼了,怎一個爽字了得!快去 My-QR。art 建立你自己的二維碼,或者去 GitHub 主頁 檢視程式碼。覺得這個專案很酷,想要助力嗎?和你的家人、朋友們分享這篇部落格,網站,去 GitHub 上為這個專案打星標,都可以。如果您手頭闊綽,若是能用網頁下方的捐贈按鈕表一份心意也是極好的,畢竟運營 My-QR。art 這個網站可不是免費的。同時,如果你用 My-QR。art 做出了酷炫的二維碼,也歡迎 透過郵件 與我分享。

> 翻譯已得到原作者(Marien Raat)授權,原文發表在 Marien Raat 的部落格上(連結)。

相關文章