因行動裝置的流行而跟著帶動起來的QR Code,是眾多條碼類型的其中一種,它讓我們的生活更便利,傳遞資訊的速度更快,省下許多使用鍵盤打字的時間。如果要產生出QR Code,已經有許多相關的現成工具能夠使用,甚至還能直接在網頁上產生。只是有的時候還是會遇到需要在自己的程式中加入產生QR Code的狀況,像是需要產生大量的QR Code時,或是想要讓自己的程式能不依賴其他工具就能獨立使用。這篇文章,將會探討如何使用Rust程式語言產生QR Code。



QR Code Generator

「QR Code Generator」是筆者開發的套件,採用「nayuki/QR-Code-generator」這個跨程式語言的QR Code產生函式庫來產生QR Code的資料矩陣,並支援RAW、SVG、PNG格式的圖檔輸出。

Crates.io

https://crates.io/crates/qrcode-generator

Cargo.toml

qrcode-generator = "*"

使用方法

qrcode_generator這個crate提供了幾個有用的函數,可以進行QR Code編碼。帶有_from_segments結尾的函數名稱,屬於低階的使用方法,可以最佳化產生出來的QR Code資料。

另外還可以使用QrCodeEcc列舉來控制QR Code資料的錯誤修正等級,其Low變體可以修正7%內的錯誤;Medium變體可以修正15%內的錯誤;Quartile變體可以修正25%內的錯誤;High變體可以修正30%內的錯誤。錯誤修正的等級愈高,編碼出來的資料量就會愈多,通常會因此使得QR Code的尺寸版本上升(白話一點來說就是QR Code的點數會變多),但是尺寸版本也有可能在錯誤修正等級的提升之下維持不變,因為資料量剛好沒超過尺寸版本的容納上限。程式設計師在使用這個套件時所指定的錯誤修正等級,這個套件會自動去選擇相同尺寸版本中「最佳」的錯誤修正等級,比如說我們想要用QrCodeEcc::Medium來編碼某個資料,但是這筆資料在使用QrCodeEcc::High編碼出來的QR Code尺寸版本和使用QrCodeEcc::Medium編碼QR Code尺寸版本是相同的話,這個套件就會選擇使用QrCodeEcc::High來作為真正的錯誤修正等級。

to_image

to_image函數可以將任意資料編碼成8位元灰階色域的圖片原始(RAW)資料,方便進行後續的影像處理。原則上輸出的資料型別為Vec<u8>,但也可以在to_image名稱後加上_buffer,來將資料以ImageBuffer<Luma<u8>, Vec<u8>>型別輸出。

程式如下:

extern crate qrcode_generator;

use qrcode_generator::QrCodeEcc;

let result: Vec<u8> = qrcode_generator::to_image("Hello world!", QrCodeEcc::Low, 1024).unwrap();

println!("{:?}", result);
to_matrix

to_matrix函數可以將任意資料編碼成Vec<Vec<bool>>型別的QR Code二維陣列,方便進行後續的處理。

程式如下:

extern crate qrcode_generator;

use qrcode_generator::QrCodeEcc;

let result: Vec<Vec<bool>> = qrcode_generator::to_matrix("Hello world!", QrCodeEcc::Low).unwrap();

println!("{:?}", result);
to_png

to_png函數可以將任意資料編碼成PNG圖片,寫進任意的Writer中。在to_png函數名稱後加上_to_vec可以將PNG圖片資料以Vec<u8>型別來輸出;加上_to_file可以將PNG圖片資料輸出成檔案。

程式如下:

extern crate qrcode_generator;

use qrcode_generator::QrCodeEcc;

qrcode_generator::to_png_to_file("Hello world!", QrCodeEcc::Low, 1024, "path/to/file.png").unwrap();
to_svg

to_svg函數可以將任意資料編碼成SVG圖片,寫進任意的Writer中。在to_svg函數名稱後加上_to_string可以將SVG圖片資料以String型別來輸出;加上_to_file可以將SVG圖片資料輸出成檔案。

程式如下:

extern crate qrcode_generator;

use qrcode_generator::QrCodeEcc;

qrcode_generator::to_svg_to_file("Hello world!", QrCodeEcc::Low, 1024, None, "path/to/file.svg").unwrap();

低階用法

nayuki/QR-Code-generator」的作者其實有提供最佳化QR Code的演算法,能夠將要編碼的資料分段,分別套用適當的編碼方式,以求最小的輸出資料量。不過這個演算法作者並沒有用Rust實作出來,所以筆者只好另外開了GitHub專案,自行將作者用Java實作的程式轉成Rust。

GitHub:

https://github.com/magiclen/qrcode-segments-optimizer

另外這個專案還支援一些能夠在QR Code中被額外優化的資料。例如網址,網址的通訊協定(或稱Scheme)部份和網域的部份,可以轉成大寫,這樣一來,該段資料在QR Code中就可以套用佔用空間比較少的Alphanumeric編碼模式,而不需用到Byte模式。

舉一個例子:

extern crate qrcode_generator;
extern crate qrcode_segments_optimizer;
extern crate url;

use qrcode_generator::QrCodeEcc;
use url::Url;

let url = "https://magiclen.org/path/to/12345";
let ecc = QrCodeEcc::Low;

let naive_matrix = qrcode_generator::to_matrix(url, ecc).unwrap();

let url = Url::parse(url).unwrap();

let optimized_matrix = qrcode_generator::to_matrix_from_segments(
    qrcode_segments_optimizer::make_segments_from_url(&url, ecc)
        .unwrap()
        .as_slice(),
    ecc,
)
.unwrap();

assert!(optimized_matrix.len() < naive_matrix.len());