Skip to main content

qrcode_generator/
lib.rs

1/*!
2# QR Code Generator
3
4This crate generates ISO/IEC 18004 QR Code and Micro QR Code symbols and ISO/IEC 23941 rMQR symbols in pure Rust, then renders them as grayscale, PNG and SVG images.
5
6You give it data and get back a ready-to-scan image. The text encoders automatically choose the shortest Numeric, Alphanumeric, Byte and optional Kanji segmentation for each candidate version, and the Model 2 and rMQR encoders also optimize ECI transitions.
7
8## Quick start
9
10Encode text and read the raw module grid:
11
12```rust
13# #[cfg(feature = "qr")] {
14use qrcode_generator::qr::{Encoder, ErrorCorrection};
15
16let symbol = Encoder::new(ErrorCorrection::Medium)
17    .encode_text("Hello, world!")
18    .unwrap();
19
20// `true` is a dark module and `false` is a light module.
21let matrix: Vec<Vec<bool>> = symbol.to_matrix();
22
23println!("{} modules per side", symbol.size());
24# }
25```
26
27Or render straight to image files:
28
29```rust,no_run
30# #[cfg(all(feature = "std", feature = "qr", feature = "image"))] {
31use qrcode_generator::{Renderer, qr::{Encoder, ErrorCorrection}};
32
33let symbol = Encoder::new(ErrorCorrection::Medium)
34    .encode_text("Hello, world!")
35    .unwrap();
36
37Renderer::new(&symbol, 512).save_svg("hello.svg", None::<&str>).unwrap();
38Renderer::new(&symbol, 512).save_png("hello.png").unwrap();
39# }
40```
41
42## Encode, then render
43
44Every workflow has two steps. An `Encoder` (from the `qr`, `micro` or `rmqr` module) turns your data into a `Symbol`, the abstract grid of modules. A `Renderer` then turns that `Symbol` into a concrete output such as an SVG string, a PNG file or a grayscale buffer. The same `Symbol` can be rendered many times at different sizes.
45
46## Key terms
47
48A few QR Code words appear throughout this documentation:
49
50- **Module** — the smallest square of a QR Code, the equivalent of one pixel. A dark module is `true` in the matrix.
51- **Matrix** — the full grid of modules. `Symbol::to_matrix` returns it as `Vec<Vec<bool>>`, and `Symbol::modules` exposes it as one flat row-major slice without copying.
52- **Symbol** — one complete QR Code, Micro QR Code or rMQR symbol.
53- **Version** — the symbol size. QR Code versions run from 1 (21×21 modules) to 40 (177×177), Micro QR Code has versions M1 to M4 (11×11 to 17×17), and rMQR has 32 rectangular sizes.
54- **Error correction level** — how much redundancy is added so a dirty or partly hidden symbol still scans. The Low, Medium, Quartile and High levels recover roughly 7%, 15%, 25% and 30% of the codewords, and a higher level is more robust but leaves less room for your own data. Micro QR Code version M1 instead uses a detection-only level that finds errors without correcting them.
55- **Mask** — a regular pattern applied over the data to avoid layouts that confuse scanners. The best mask is chosen automatically, so you rarely set it yourself.
56- **Quiet zone** — the plain margin around the symbol that scanners need. It defaults to 4 modules for QR Code and 2 for Micro QR Code and rMQR.
57- **Mode / segment** — how characters are packed. Numeric is the most compact, then Alphanumeric, then Byte, plus optional Kanji, and the encoder mixes them automatically to save space.
58- **ECI** — an Extended Channel Interpretation header that declares the character set, such as UTF-8, when it is not the default ISO-8859-1.
59
60## Encoding
61
62`encode_text` interprets the input as ISO-8859-1 when possible and adds a UTF-8 ECI header only when it is needed. The C1 control characters U+0080 to U+009F are not in the byte table of ISO/IEC 18004, so they use UTF-8 as well:
63
64```rust
65# #[cfg(feature = "qr")] {
66use qrcode_generator::qr::{Encoder, ErrorCorrection};
67
68let symbol = Encoder::new(ErrorCorrection::Medium)
69    .encode_text("café ☕")
70    .unwrap();
71# let _ = symbol;
72# }
73```
74
75`encode_bytes` stores an exact binary payload and never guesses a character set:
76
77```rust
78# #[cfg(feature = "qr")] {
79use qrcode_generator::qr::{Encoder, ErrorCorrection};
80
81let symbol = Encoder::new(ErrorCorrection::Quartile)
82    .encode_bytes(b"raw\0bytes")
83    .unwrap();
84# let _ = symbol;
85# }
86```
87
88The encoder is configured with a builder. You can restrict the versions it may use, or force a specific mask:
89
90```rust
91# #[cfg(feature = "qr")] {
92use qrcode_generator::qr::{Encoder, ErrorCorrection, Version};
93
94let symbol = Encoder::new(ErrorCorrection::Low)
95    .version_range(Version::new(1).unwrap()..=Version::new(10).unwrap())
96    .encode_text("HELLO")
97    .unwrap();
98# let _ = symbol;
99# }
100```
101
102By default the encoder upgrades the error correction level for free when the chosen version has spare room. Call `boost_error_correction(false)` to keep the exact level you requested.
103
104### Explicit segments
105
106Automatic segmentation is optimal for most inputs, but you can also build `Segment` values yourself and keep their boundaries:
107
108```rust
109# #[cfg(feature = "qr")] {
110use qrcode_generator::{Segment, qr::{Encoder, ErrorCorrection}};
111
112let segments = [
113    Segment::numeric("1234567").unwrap(),
114    Segment::alphanumeric("ABCDEFG").unwrap(),
115];
116
117let symbol = Encoder::new(ErrorCorrection::Low)
118    .encode_segments(&segments)
119    .unwrap();
120# let _ = symbol;
121# }
122```
123
124### Custom text
125
126[`ToQRText`] lets a structured value choose a shorter equivalent spelling before the optimizer runs:
127
128```rust
129# #[cfg(feature = "qr")] {
130use qrcode_generator::{ToQRText, qr::{Encoder, ErrorCorrection}};
131
132struct ProductCode(&'static str);
133
134impl ToQRText for ProductCode {
135    fn to_qr_text(&self) -> String {
136        self.0.to_ascii_uppercase()
137    }
138}
139
140let symbol = Encoder::new(ErrorCorrection::Medium)
141    .encode_to_qr_text(&ProductCode("item-123"))
142    .unwrap();
143# let _ = symbol;
144# }
145```
146
147The value must keep its meaning. With the optional `url` feature, `url::Url` implements `ToQRText` by normalizing case-insensitive URL parts and percent escapes:
148
149```rust
150# #[cfg(all(feature = "qr", feature = "url"))] {
151use qrcode_generator::qr::{Encoder, ErrorCorrection};
152use url::Url;
153
154let url = Url::parse("https://magiclen.org").unwrap();
155let symbol = Encoder::new(ErrorCorrection::Medium).encode_to_qr_text(&url).unwrap();
156# let _ = symbol;
157# }
158```
159
160## Rendering
161
162A `Renderer` draws a `Symbol` at exact pixel dimensions. `Renderer::new` keeps the square interface, while `Renderer::new_with_dimensions` accepts a separate width and height for rectangular symbols. In-memory grayscale and SVG output work with `no_std + alloc` and need no extra feature:
163
164```rust
165# #[cfg(feature = "qr")] {
166use qrcode_generator::{Renderer, qr::{Encoder, ErrorCorrection}};
167
168let symbol = Encoder::new(ErrorCorrection::Low).encode_text("Hello").unwrap();
169
170let svg: String = Renderer::new(&symbol, 512)
171    .svg_xml_declaration(false)
172    .to_svg_string(None::<&str>)
173    .unwrap();
174let pixels: Vec<u8> = Renderer::new(&symbol, 512).to_luma8().unwrap();
175# let _ = (svg, pixels);
176# }
177```
178
179SVG output includes an XML declaration by default. Use `svg_xml_declaration(false)` to start the output with `<svg>` instead. This setting applies to strings, writers and saved files, including async output.
180
181The renderer also implements `Display`, drawing the symbol as compact Unicode text that packs two module rows into every line through half block characters, including the quiet zone. The default form draws dark modules as visible blocks; the alternate form (`{:#}`) draws light modules instead, which keeps the standard contrast on dark terminal backgrounds. Text output works in module units, so the pixel size passed to the renderer does not affect it:
182
183```rust
184# #[cfg(feature = "qr")] {
185use qrcode_generator::{Renderer, qr::{Encoder, ErrorCorrection}};
186
187let symbol = Encoder::new(ErrorCorrection::Low).encode_text("Hello").unwrap();
188let renderer = Renderer::new(&symbol, 0);
189
190// For light terminal backgrounds.
191print!("{renderer}");
192
193// For dark terminal backgrounds.
194print!("{renderer:#}");
195# }
196```
197
198PNG and `ImageBuffer` output need the default `image` feature:
199
200```rust,no_run
201# #[cfg(all(feature = "std", feature = "qr", feature = "image"))] {
202use qrcode_generator::{Renderer, qr::{Encoder, ErrorCorrection}};
203
204let symbol = Encoder::new(ErrorCorrection::Low).encode_text("Hello").unwrap();
205
206let png: Vec<u8> = Renderer::new(&symbol, 512).to_png_vec().unwrap();
207
208Renderer::new(&symbol, 512).save_png("hello.png").unwrap();
209# let _ = png;
210# }
211```
212
213The default `std` feature adds synchronous writer and file output. File output through `save_svg` and `save_png` is written atomically, so an existing file is left untouched if rendering fails. The requested size is exact: modules are scaled by the largest whole number that fits, and any pixels left over widen the quiet zone evenly. Use `quiet_zone` to change the margin, down to zero if you want.
214
215## Async writing
216
217The optional `tokio` feature adds `write_svg_async` and, with the `image` feature, `write_png_async`, which write and flush to any `tokio::io::AsyncWrite`, including a `tokio::fs::File`. Rendering stays synchronous; only writing and flushing are asynchronous:
218
219```rust,no_run
220# #[cfg(all(feature = "qr", feature = "tokio"))]
221# mod example {
222use qrcode_generator::{
223    RenderError, Renderer,
224    qr::{Encoder, ErrorCorrection},
225};
226
227async fn write<W: tokio::io::AsyncWrite + Unpin>(writer: W) -> Result<(), RenderError> {
228    let symbol = Encoder::new(ErrorCorrection::Low).encode_text("Hello").unwrap();
229
230    Renderer::new(&symbol, 512).write_svg_async(writer, None::<&str>).await
231}
232# }
233```
234
235The feature also adds `save_svg_async` and `save_png_async`, which save to a path atomically like their synchronous counterparts by running the write on tokio's blocking pool.
236
237## Micro QR Code
238
239The optional `micro-qr` feature adds an independent encoder for the four Micro QR Code versions, which are smaller than QR Code and suit short payloads:
240
241```rust
242# #[cfg(feature = "micro-qr")] {
243use qrcode_generator::micro::{Encoder, ErrorCorrection, Version};
244
245let symbol = Encoder::new(ErrorCorrection::Low)
246    .version(Version::M2)
247    .encode_text("12345")
248    .unwrap();
249# let _ = symbol;
250# }
251```
252
253Version M1 supports only the `DetectionOnly` error correction level, which detects errors without correcting them; M2 and M3 add Low and Medium, and M4 also offers Quartile.
254
255Micro QR Code does not support ECI, FNC1 or Structured Append. Its text API accepts ISO-8859-1 except the C1 control characters and, with the `kanji` feature, eligible Shift JIS Kanji characters. Because Micro QR Code has no ECI, a symbol that uses Kanji mode cannot also hold the bytes E0 to EB (à to ë).
256
257## Rectangular Micro QR Code
258
259The optional `rmqr` feature adds all 32 ISO/IEC 23941 rMQR versions. The encoder automatically selects the smallest-area version that fits and performs exact segment and ECI optimization for each character-count profile:
260
261```rust
262# #[cfg(feature = "rmqr")] {
263use qrcode_generator::{Renderer, rmqr::{Encoder, ErrorCorrection}};
264
265let symbol = Encoder::new(ErrorCorrection::Medium)
266    .encode_text("https://magiclen.org")
267    .unwrap();
268
269let width = (symbol.width() + 4) * 8;
270let height = (symbol.height() + 4) * 8;
271let svg = Renderer::new_with_dimensions(&symbol, width, height)
272    .to_svg_string(None::<&str>)
273    .unwrap();
274# }
275```
276
277rMQR supports Medium and High error correction, ECI, FNC1 and optional Kanji mode. It has one fixed data mask and does not support Structured Append.
278
279## Automatic family selection
280
281With both `qr` and `micro-qr` enabled, `AutoEncoder` tries the Micro QR Code encoder first and falls back to the Model 2 encoder when the data does not fit, giving you the smallest symbol that works:
282
283```rust
284# #[cfg(all(feature = "qr", feature = "micro-qr"))] {
285use qrcode_generator::{AutoEncoder, micro, qr};
286
287let encoder = AutoEncoder::new(
288    qr::Encoder::new(qr::ErrorCorrection::Medium),
289    micro::Encoder::new(micro::ErrorCorrection::Medium),
290);
291
292let symbol = encoder.encode_text("HELLO 123").unwrap();
293# let _ = symbol;
294# }
295```
296
297If the Model 2 encoder has FNC1 enabled, `AutoEncoder` uses it directly because Micro QR Code cannot carry FNC1.
298
299## FNC1 and Structured Append
300
301These two QR Code features serve data-exchange standards rather than plain text.
302
303*FNC1* marks a symbol as following a formatted-data standard rather than plain text. First position selects GS1, whose element strings concatenate application identifiers and separate variable-length ones with a `0x1D` (GS) byte; second position selects an industry application indicator through `ApplicationIndicator`. Pass the data with the `0x1D` separators already in place and turn FNC1 on with `qr::Encoder::fnc1`:
304
305```rust
306# #[cfg(feature = "qr")] {
307use qrcode_generator::qr::{Encoder, ErrorCorrection, Fnc1};
308
309// AI 01 is a GTIN, the 0x1D (GS) byte separates it, then AI 10 is a batch number.
310let symbol = Encoder::new(ErrorCorrection::Medium)
311    .fnc1(Some(Fnc1::Gs1))
312    .encode_bytes(b"0101234567890128\x1D10ABC")
313    .unwrap();
314# let _ = symbol;
315# }
316```
317
318Alphanumeric mode has no value for `0x1D`, so the encoder writes a separator as `%` and doubles a literal `%` to `%%`. It also starts a new segment when a separator is followed by another separator or by a literal `%`, because a reader would otherwise pair those two characters into one literal `%`. Byte mode stores both characters unchanged.
319
320*Structured Append* spreads one message across up to 16 symbols that a reader stitches back together. You can supply the parts yourself, or let the encoder split a message automatically:
321
322```rust,no_run
323# #[cfg(feature = "qr")] {
324use qrcode_generator::qr::{Encoder, ErrorCorrection};
325
326let symbols = Encoder::new(ErrorCorrection::Medium)
327    .encode_text_with_structured_append("a very long message …")
328    .unwrap();
329
330println!("{} symbols", symbols.len());
331# }
332```
333
334Automatic splitting first minimizes the number of symbols, then their largest version, and finally the total symbol area. A reader joins the parts into one data stream, so the text encoders declare an ECI only from the part that first needs it, repeat it at the start of every later part, and use Kanji mode without an ECI header only in a sequence that has no ECI header at all.
335
336With `encode_structured_append_segments`, each part must include its own ECI headers before the data that needs them; ECI state is not copied from earlier parts.
337
338## Kanji
339
340The optional `kanji` feature adds full Shift JIS support to automatic text segmentation and exposes `Segment::kanji`. Eligible characters use the compact 13-bit Kanji mode, and other Shift JIS text such as half-width katakana can be stored as Shift JIS bytes behind an ECI 000020 header. ISO/IEC 18004 reads data before the first ECI header with the default ISO-8859-1 interpretation, so Kanji mode without an ECI header is only used in a symbol that has no ECI header at all, and that symbol keeps its bytes clear of E0 to EB, which readers could take for Shift JIS lead bytes. Any other symbol declares an explicit Shift JIS ECI before its Kanji data. The feature is off by default for the widest scanner compatibility.
341
342Text must keep its original characters after decoding, so Shift JIS follows the character sets that ISO/IEC 18004 names for ECI 000020: JIS X 0208 for two-byte characters and JIS8 for single bytes, where byte 5C is the yen sign and byte 7E is the overline. ISO/IEC 18004 defines no Unicode mapping, and published mappings disagree on eight JIS X 0208 positions. For example, 0x817C reads as U+2212 (minus sign) in JIS-based tables and as U+FF0D (fullwidth hyphen-minus) in Windows-based tables, so neither character uses it. Characters outside JIS X 0208, such as the NEC and IBM extensions ① and 髙, are not used either. QR Code and rMQR store these characters as UTF-8, while Micro QR Code and `Segment::kanji` reject them.
343
344## Cargo features
345
346- `std` (default) enables synchronous writer and file output.
347- `qr` (default) enables the Model 2 QR Code encoder.
348- `image` (default, requires `std`) enables PNG and `ImageBuffer` output.
349- `micro-qr` enables the Micro QR Code encoder with all four versions.
350- `rmqr` enables the Rectangular Micro QR Code encoder with all 32 versions.
351- `kanji` enables Shift JIS Kanji segments and automatic Kanji mode selection.
352- `url` implements `ToQRText` for `url::Url` and works with `no_std + alloc`.
353- `tokio` (requires `std`) enables the tokio asynchronous writer and atomic file-save methods.
354*/
355
356#![cfg_attr(not(feature = "std"), no_std)]
357#![cfg_attr(docsrs, feature(doc_cfg))]
358
359extern crate alloc;
360#[cfg(test)]
361extern crate std;
362
363mod encode;
364mod error;
365#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
366mod render;
367#[cfg(feature = "url")]
368mod url;
369
370#[cfg(all(feature = "qr", feature = "micro-qr"))]
371pub use encode::AutoEncoder;
372pub use encode::{Segment, ToQRText};
373#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
374pub use encode::{Symbol, SymbolErrorCorrection, SymbolVersion};
375pub use error::{EncodeError, RenderError};
376#[cfg(any(feature = "qr", feature = "micro-qr", feature = "rmqr"))]
377pub use render::Renderer;
378
379/// Model 2 QR Code encoding types.
380#[cfg(feature = "qr")]
381pub mod qr {
382    pub use crate::encode::{
383        ApplicationIndicator, EciAssignment, Fnc1, QrEncoder as Encoder,
384        QrErrorCorrection as ErrorCorrection, QrMask as Mask, QrVersion as Version,
385        StructuredAppendInfo,
386    };
387}
388
389/// Micro QR Code encoding types.
390#[cfg(feature = "micro-qr")]
391pub mod micro {
392    pub use crate::encode::{
393        MicroEncoder as Encoder, MicroErrorCorrection as ErrorCorrection, MicroMask as Mask,
394        MicroVersion as Version,
395    };
396}
397
398/// Rectangular Micro QR Code encoding types.
399#[cfg(feature = "rmqr")]
400pub mod rmqr {
401    pub use crate::encode::{
402        ApplicationIndicator, EciAssignment, Fnc1, RmqrEncoder as Encoder,
403        RmqrErrorCorrection as ErrorCorrection, RmqrVersion as Version,
404    };
405}