對於寫程式的我,並不認為 QR code 的產生是不可行的,只要有已經釋出的函數庫,或是不太難理解的演算規則,產生出圖片應該不成問題。尤其是最近因為專案因素對於使用 Action Script 繪圖有不少的實作機會,更是增加自己不少信心,不過現實總是有些不如預期,繞了一些遠路後才找到解法。
在快要下決定研究 QR code 生成的數學公式時,還好發現在 Google Codes 裡發現有人提供的函數庫,讓我免於研究那些可能會讓人煩燥的數學公式,不過函數庫裡有小 Bug,以至不支援中文字,還好最後查到問題點,在這裡作一下記錄…
在 Google Code 上查到一個叫作「zxing」的專案,提供許多種程式語言不同的版本的條碼演算法,由於是 Apache 2.0 的授權方式,所以就安心的放到測試專案中。這次我下載的是 2.0 的版本,由於網路上並沒有關於 Action Script 版本的詳細說明(印象中有看到一個英文的,不過當時沒有看懂,靠自己研究出用法),因此萠生我打這篇文章的念頭。
下載的檔案包含許多程式語言的版本,在這邊我只取我需要的原始碼的部分…
建立一個專案,開始撰寫程式…
因為程式有一點小 Bug ,所以得抓原始碼進來,而不是先作成 swc 變成函數庫引用。
其中一個滿特別的現象,就是套件的命名是由「com.google」開頭,不過在專案中並沒有看到明顯由 Google 主導或贊助的字樣,所以不能判定和 Google 是不是有什麼關係。不過這不是本篇的重點,還是將焦點擺回程式上。
在 com.google.zxing 套件裡有一個叫作 Writer 的介面,判斷應該是所有編碼器的方法定義。同時在 com.google.zxing.qrcode.下看到一個叫作 QRCodeWriter 的類別實作它,所以看起來應該是使用它,底下將介面所定義的唯一的方法作簡單的翻譯…
/** * * @param contents 要進行編碼的內容 * @param format 編碼的格式 * @param width 編碼結果的寬度(像素) * @param height 編碼結果的高度(像素) * @param hints 其它編輯器所有支援的附加參數 * @return 使用無號整數組成的矩陣表示編碼結果 (0 == 黑, 255 == 白) */ function encode(contents:String, format:BarcodeFormat=null, width:int=0, height:int=0, hints:HashTable=null):Object;以 QR Code 來說,只會用到1, 2, 5 三個參數,就實作中猜測 width 和 height 兩個參數應該是沒有影響,不過這兩個參數著實讓我繞了一些遠路,讓我一開始認為回傳回一個類似 BitmapData 之類的傳回值。結果是一個矩陣,由於在介面中定義的傳回值是 Object ,所以得到 QRCodeWriter 的程式碼裡找答案,結果原來是另一個叫作「BitMatrix」的類別,的的確確是一個「矩陣」資料。不過和介面定義的不同,並不是用 byte 而是使用布林值來表示。使用 _get 方法取得每個位置的資料,以下是例子…
0 0 0 0 0 0 0 ░░░░░░░ 0 1 1 1 1 1 0 ░█████░ 0 1 0 0 0 1 0 ░█░░░█░ 0 1 0 1 0 1 0 ░█░█░█░ 0 1 0 0 0 1 0 ░█░░░█░ 0 1 1 1 1 1 0 ░█████░ 0 0 0 0 0 0 0 ░░░░░░░以上面的例子,由 _get(3, 3) 得到的值將是 false(白色)
以下我是一開始的程式碼…其中使用一個 Group 物件作畫布,將結果畫出來。
/** * 在「建立」按鈕按下後,進行編碼 */ private function createQRCode(event:MouseEvent):void { var writer:Writer = new QRCodeWriter(); // 建立編碼器 // 變數 content 是我建立的一個 TextInput 物件,用來輸入要編碼的資料 var mb:BitMatrix = writer.encode(content.text, BarcodeFormat.QR_CODE) as BitMatrix; var width:int = mb.width; // ?向有多少"格" var height:int = mb.height; // 縱向有多少"格" // 不過因為 QRCode 在規則上是正方形的,所以理論上兩個數值是一樣的 var color:uint; // 畫圖用的資料 // 取得畫布大小 var cw:Number = canvas.width; var ch:Number = canvas.height; // 這裡使這兩個變數來表繪圖的起始點 var sx:Number; var sy:Number; // 由於畫布不見得是正方形的,所以較小的數字為基準 if(cw > ch) { sx = (cw - ch)/2; sy = 0; cw = ch; } else { sy = (ch - cw)/2; sx = 0; ch = cw; } canvas.graphics.clear(); // 清空之前畫的東西 for(var y:int=0; y<height; y++) { for(var x:int=0; x<width; x++) { color = mb._get(x, y)?0:0xFFFFFF; canvas.graphics.beginFill(color, 1); canvas.graphics.drawRect( sx+cw/width*x, sy+ch/height*y, // 計算格子的位置(座標) cw/width, ch/height // 計算格子的大小 ); canvas.graphics.endFill(); } } }
附上一張程式截圖…
因為程式碼真的很難編輯,下一篇再說明如何解決中文字的問題…
要如何才能识别中文字呢?
回覆刪除