qrcode_generator/
lib.rs

1/*!
2# QR Code Generator
3
4This crate provides functions to generate QR Code matrices and images in RAW, PNG and SVG formats.
5
6## Examples
7
8#### Encode any data to a QR Code matrix which is `Vec<Vec<bool>>`.
9
10```rust
11use qrcode_generator::QrCodeEcc;
12
13let result: Vec<Vec<bool>> = qrcode_generator::to_matrix("Hello world!", QrCodeEcc::Low).unwrap();
14
15println!("{:?}", result);
16```
17
18#### Encode any data to a PNG image stored in a Vec instance.
19
20```rust
21use qrcode_generator::QrCodeEcc;
22
23# #[cfg(feature = "image")] {
24let result: Vec<u8> = qrcode_generator::to_png_to_vec("Hello world!", QrCodeEcc::Low, 1024).unwrap();
25
26println!("{:?}", result);
27# }
28```
29
30#### Encode any data to a PNG image stored in a file.
31
32```rust
33use qrcode_generator::QrCodeEcc;
34
35# #[cfg(feature = "image")] {
36qrcode_generator::to_png_to_file("Hello world!", QrCodeEcc::Low, 1024, "tests/data/file_output.png").unwrap();
37# }
38```
39
40#### Encode any data to a SVG image stored in a String instance.
41
42```rust
43use qrcode_generator::QrCodeEcc;
44
45let result: String = qrcode_generator::to_svg_to_string("Hello world!", QrCodeEcc::Low, 1024, None::<&str>).unwrap();
46
47println!("{:?}", result);
48```
49
50#### Encode any data to a SVG image stored in a file.
51
52```rust
53use qrcode_generator::QrCodeEcc;
54
55qrcode_generator::to_svg_to_file("Hello world!", QrCodeEcc::Low, 1024, None::<&str>, "tests/data/file_output.png").unwrap();
56```
57
58## Low-level Usage
59
60### Raw Image Data
61
62The `to_image` and `to_image_buffer` functions can be used, if you want to modify your image.
63
64### Segments
65
66Every `to_*` function has a corresponding `_from_segments` function. You can concatenate segments by using different encoding methods, such as **numeric**, **alphanumeric** or **binary** to reduce the size (level) of your QR code matrix/image.
67
68```rust
69use qrcode_generator::{QrCodeEcc, QrSegment};
70
71let first = "1234567";
72
73let second = "ABCDEFG";
74
75let segments = [QrSegment::make_numeric(&first), QrSegment::make_alphanumeric(&second)];
76
77let result: Vec<Vec<bool>> = qrcode_generator::to_matrix_from_segments(&segments, QrCodeEcc::Low).unwrap();
78
79println!("{:?}", result);
80```
81
82More segments optimization apporaches: [magiclen/qrcode-segments-optimizer](https://github.com/magiclen/qrcode-segments-optimizer)
83*/
84
85pub extern crate qrcodegen;
86
87mod qr_code_error;
88
89use core::{mem::size_of, str::from_utf8};
90use std::{
91    fs::{self, File},
92    io::Write,
93    path::Path,
94};
95
96#[cfg(feature = "image")]
97use image::codecs::png::{CompressionType, FilterType, PngEncoder};
98#[cfg(feature = "image")]
99use image::{ColorType, ImageBuffer, ImageEncoder, Luma};
100pub use qr_code_error::*;
101use qrcodegen::QrCode;
102pub use qrcodegen::{QrCodeEcc, QrSegment};
103
104#[inline]
105fn generate_qrcode<D: AsRef<[u8]>>(data: D, ecc: QrCodeEcc) -> Result<QrCode, QRCodeError> {
106    match from_utf8(data.as_ref()) {
107        Ok(text) => generate_qrcode_from_str(text, ecc),
108        Err(_) => {
109            let qr = match QrCode::encode_binary(data.as_ref(), ecc) {
110                Ok(qr) => qr,
111                Err(_) => return Err(QRCodeError::DataTooLong),
112            };
113
114            Ok(qr)
115        },
116    }
117}
118
119#[inline]
120fn generate_qrcode_from_str<S: AsRef<str>>(text: S, ecc: QrCodeEcc) -> Result<QrCode, QRCodeError> {
121    let qr = match QrCode::encode_text(text.as_ref(), ecc) {
122        Ok(qr) => qr,
123        Err(_) => return Err(QRCodeError::DataTooLong),
124    };
125
126    Ok(qr)
127}
128
129#[inline]
130fn generate_qrcode_from_segments(
131    segments: &[QrSegment],
132    ecc: QrCodeEcc,
133) -> Result<QrCode, QRCodeError> {
134    let qr = match QrCode::encode_segments(segments, ecc) {
135        Ok(qr) => qr,
136        Err(_) => return Err(QRCodeError::DataTooLong),
137    };
138
139    Ok(qr)
140}
141
142#[inline]
143fn to_matrix_inner(qr: QrCode) -> Vec<Vec<bool>> {
144    let size = qr.size();
145
146    let size_u = size as usize;
147
148    let mut rows = Vec::with_capacity(size_u);
149
150    for y in 0..size {
151        let mut row = Vec::with_capacity(size_u);
152
153        for x in 0..size {
154            row.push(qr.get_module(x, y));
155        }
156
157        rows.push(row);
158    }
159
160    rows
161}
162
163#[inline]
164fn to_svg_inner<S: AsRef<str>, W: Write>(
165    qr: QrCode,
166    size: usize,
167    description: Option<S>,
168    mut writer: W,
169) -> Result<(), QRCodeError> {
170    let margin_size = 1;
171
172    let s = qr.size();
173
174    let data_length = s as usize;
175
176    let data_length_with_margin = data_length + 2 * margin_size;
177
178    let point_size = size / data_length_with_margin;
179
180    if point_size == 0 {
181        return Err(QRCodeError::ImageSizeTooSmall);
182    }
183
184    let margin = (size - (point_size * data_length)) / 2;
185
186    writer.write_fmt(format_args!("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"{size}\" height=\"{size}\" shape-rendering=\"crispEdges\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"))?;
187
188    match description {
189        Some(description) => {
190            let description = description.as_ref();
191
192            if !description.is_empty() {
193                writer.write_all(b"\t<desc>")?;
194                html_escape::encode_safe_to_writer(description, &mut writer)?;
195                writer.write_all(b"</desc>\n")?;
196            }
197        },
198        None => {
199            writer.write_fmt(format_args!(
200                "\t<desc>{name} {version} by magiclen.org</desc>\n",
201                name = env!("CARGO_PKG_NAME"),
202                version = env!("CARGO_PKG_VERSION")
203            ))?;
204        },
205    }
206
207    writer.write_fmt(format_args!(
208        "\t<rect width=\"{size}\" height=\"{size}\" fill=\"#FFF\"/>\n\t<path d=\""
209    ))?;
210
211    for i in 0..s {
212        for j in 0..s {
213            if qr.get_module(j, i) {
214                let x = j as usize * point_size + margin;
215                let y = i as usize * point_size + margin;
216
217                writer.write_fmt(format_args!("M{x} {y}h{point_size}v{point_size}H{x}V{y}"))?;
218            }
219        }
220    }
221
222    writer.write_all(b"\"/>\n</svg>")?;
223
224    writer.flush()?;
225
226    Ok(())
227}
228
229#[inline]
230fn to_svg_to_vec_inner<S: AsRef<str>>(
231    qr: QrCode,
232    size: usize,
233    description: Option<S>,
234) -> Result<Vec<u8>, QRCodeError> {
235    let mut svg = Vec::with_capacity(32768);
236
237    let margin_size = 1;
238
239    let s = qr.size();
240
241    let data_length = s as usize;
242
243    let data_length_with_margin = data_length + 2 * margin_size;
244
245    let point_size = size / data_length_with_margin;
246
247    if point_size == 0 {
248        return Err(QRCodeError::ImageSizeTooSmall);
249    }
250
251    let margin = (size - (point_size * data_length)) / 2;
252
253    svg.write_fmt(format_args!("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<svg width=\"{size}\" height=\"{size}\" shape-rendering=\"crispEdges\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"))?;
254
255    match description {
256        Some(description) => {
257            let description = description.as_ref();
258
259            if !description.is_empty() {
260                svg.extend_from_slice(b"\t<desc>");
261                html_escape::encode_safe_to_writer(description, &mut svg)?;
262                svg.extend_from_slice(b"</desc>\n");
263            }
264        },
265        None => {
266            svg.write_fmt(format_args!(
267                "\t<desc>{name} {version} by magiclen.org</desc>\n",
268                name = env!("CARGO_PKG_NAME"),
269                version = env!("CARGO_PKG_VERSION")
270            ))?;
271        },
272    }
273
274    svg.write_fmt(format_args!(
275        "\t<rect width=\"{size}\" height=\"{size}\" fill=\"#FFF\"/>\n\t<path d=\""
276    ))?;
277
278    for i in 0..s {
279        for j in 0..s {
280            if qr.get_module(j, i) {
281                let x = j as usize * point_size + margin;
282                let y = i as usize * point_size + margin;
283
284                svg.write_fmt(format_args!("M{x} {y}h{point_size}v{point_size}H{x}V{y}"))?;
285            }
286        }
287    }
288
289    svg.write_all(b"\"/>\n</svg>")?;
290
291    Ok(svg)
292}
293
294#[inline]
295fn to_svg_to_string_inner<S: AsRef<str>>(
296    qr: QrCode,
297    size: usize,
298    description: Option<S>,
299) -> Result<String, QRCodeError> {
300    let svg = to_svg_to_vec_inner(qr, size, description)?;
301
302    Ok(unsafe { String::from_utf8_unchecked(svg) })
303}
304
305#[inline]
306fn to_svg_to_file_inner<S: AsRef<str>, P: AsRef<Path>>(
307    qr: QrCode,
308    size: usize,
309    description: Option<S>,
310    path: P,
311) -> Result<(), QRCodeError> {
312    let path = path.as_ref();
313
314    let file = File::create(path)?;
315
316    to_svg_inner(qr, size, description, file).map_err(|err| {
317        if fs::remove_file(path).is_err() {
318            // do nothing
319        }
320        err
321    })
322}
323
324fn to_image_inner(qr: QrCode, size: usize) -> Result<Vec<u8>, QRCodeError> {
325    if size >= 2usize.pow((size_of::<usize>() * 4) as u32) {
326        return Err(QRCodeError::ImageSizeTooLarge);
327    }
328
329    let margin_size = 1;
330
331    let s = qr.size();
332
333    let data_length = s as usize;
334
335    let data_length_with_margin = data_length + 2 * margin_size;
336
337    let point_size = size / data_length_with_margin;
338
339    if point_size == 0 {
340        return Err(QRCodeError::ImageSizeTooSmall);
341    }
342
343    let margin = (size - (point_size * data_length)) / 2;
344
345    let length = size * size;
346
347    let mut img_raw: Vec<u8> = vec![255u8; length];
348
349    for i in 0..s {
350        for j in 0..s {
351            if qr.get_module(i, j) {
352                let x = i as usize * point_size + margin;
353                let y = j as usize * point_size + margin;
354
355                for j in y..(y + point_size) {
356                    let offset = j * size;
357                    for i in x..(x + point_size) {
358                        img_raw[offset + i] = 0;
359                    }
360                }
361            }
362        }
363    }
364
365    Ok(img_raw)
366}
367
368#[cfg(feature = "image")]
369#[inline]
370fn to_png_inner<W: Write>(qr: QrCode, size: usize, writer: W) -> Result<(), QRCodeError> {
371    let img_raw = to_image_inner(qr, size)?;
372
373    let encoder = PngEncoder::new_with_quality(writer, CompressionType::Best, FilterType::NoFilter);
374
375    Ok(encoder.write_image(&img_raw, size as u32, size as u32, ColorType::L8.into())?)
376}
377
378#[cfg(feature = "image")]
379#[inline]
380fn to_png_to_vec_inner(qr: QrCode, size: usize) -> Result<Vec<u8>, QRCodeError> {
381    let mut png = Vec::with_capacity(4096);
382
383    to_png_inner(qr, size, &mut png)?;
384
385    Ok(png)
386}
387
388#[cfg(feature = "image")]
389#[inline]
390fn to_png_to_file_inner<P: AsRef<Path>>(
391    qr: QrCode,
392    size: usize,
393    path: P,
394) -> Result<(), QRCodeError> {
395    let path = path.as_ref();
396
397    let file = File::create(path)?;
398
399    to_png_inner(qr, size, file).map_err(|err| {
400        if fs::remove_file(path).is_err() {
401            // do nothing
402        }
403        err
404    })
405}
406
407#[cfg(feature = "image")]
408#[inline]
409fn to_image_buffer_inner(
410    qr: QrCode,
411    size: usize,
412) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
413    let img_raw = to_image_inner(qr, size)?;
414
415    let img: ImageBuffer<Luma<u8>, Vec<u8>> =
416        ImageBuffer::from_vec(size as u32, size as u32, img_raw).unwrap();
417
418    Ok(img)
419}
420
421// TODO public functions
422
423/// Encode data to a QR code matrix.
424#[inline]
425pub fn to_matrix<D: AsRef<[u8]>>(data: D, ecc: QrCodeEcc) -> Result<Vec<Vec<bool>>, QRCodeError> {
426    Ok(to_matrix_inner(generate_qrcode(data, ecc)?))
427}
428
429/// Encode text to a QR code matrix.
430#[inline]
431pub fn to_matrix_from_str<S: AsRef<str>>(
432    text: S,
433    ecc: QrCodeEcc,
434) -> Result<Vec<Vec<bool>>, QRCodeError> {
435    Ok(to_matrix_inner(generate_qrcode_from_str(text, ecc)?))
436}
437
438/// Encode segments to a QR code matrix.
439#[inline]
440pub fn to_matrix_from_segments(
441    segments: &[QrSegment],
442    ecc: QrCodeEcc,
443) -> Result<Vec<Vec<bool>>, QRCodeError> {
444    Ok(to_matrix_inner(generate_qrcode_from_segments(segments, ecc)?))
445}
446
447/// Encode data to raw image in memory.
448pub fn to_image<D: AsRef<[u8]>>(
449    data: D,
450    ecc: QrCodeEcc,
451    size: usize,
452) -> Result<Vec<u8>, QRCodeError> {
453    to_image_inner(generate_qrcode(data, ecc)?, size)
454}
455
456/// Encode text to raw image in memory.
457pub fn to_image_from_str<S: AsRef<str>>(
458    text: S,
459    ecc: QrCodeEcc,
460    size: usize,
461) -> Result<Vec<u8>, QRCodeError> {
462    to_image_inner(generate_qrcode_from_str(text, ecc)?, size)
463}
464
465/// Encode segments to raw image in memory.
466pub fn to_image_from_segments(
467    segments: &[QrSegment],
468    ecc: QrCodeEcc,
469    size: usize,
470) -> Result<Vec<u8>, QRCodeError> {
471    to_image_inner(generate_qrcode_from_segments(segments, ecc)?, size)
472}
473
474/// Encode data to a SVG image in memory.
475#[inline]
476pub fn to_svg_to_string<D: AsRef<[u8]>, DESC: AsRef<str>>(
477    data: D,
478    ecc: QrCodeEcc,
479    size: usize,
480    description: Option<DESC>,
481) -> Result<String, QRCodeError> {
482    to_svg_to_string_inner(generate_qrcode(data, ecc)?, size, description)
483}
484
485/// Encode text to a SVG image in memory.
486#[inline]
487pub fn to_svg_to_string_from_str<S: AsRef<str>, DESC: AsRef<str>>(
488    text: S,
489    ecc: QrCodeEcc,
490    size: usize,
491    description: Option<DESC>,
492) -> Result<String, QRCodeError> {
493    to_svg_to_string_inner(generate_qrcode_from_str(text, ecc)?, size, description)
494}
495
496/// Encode segments to a SVG image in memory.
497#[inline]
498pub fn to_svg_to_string_from_segments<DESC: AsRef<str>>(
499    segments: &[QrSegment],
500    ecc: QrCodeEcc,
501    size: usize,
502    description: Option<DESC>,
503) -> Result<String, QRCodeError> {
504    to_svg_to_string_inner(generate_qrcode_from_segments(segments, ecc)?, size, description)
505}
506
507/// Encode data to a SVG image via a file path.
508#[inline]
509pub fn to_svg_to_file<D: AsRef<[u8]>, DESC: AsRef<str>, P: AsRef<Path>>(
510    data: D,
511    ecc: QrCodeEcc,
512    size: usize,
513    description: Option<DESC>,
514    path: P,
515) -> Result<(), QRCodeError> {
516    to_svg_to_file_inner(generate_qrcode(data, ecc)?, size, description, path)
517}
518
519/// Encode text to a SVG image via a file path.
520#[inline]
521pub fn to_svg_to_file_from_str<S: AsRef<str>, DESC: AsRef<str>, P: AsRef<Path>>(
522    text: S,
523    ecc: QrCodeEcc,
524    size: usize,
525    description: Option<DESC>,
526    path: P,
527) -> Result<(), QRCodeError> {
528    to_svg_to_file_inner(generate_qrcode_from_str(text, ecc)?, size, description, path)
529}
530
531/// Encode segments to a SVG image via a file path.
532#[inline]
533pub fn to_svg_to_file_from_segments<DESC: AsRef<str>, P: AsRef<Path>>(
534    segments: &[QrSegment],
535    ecc: QrCodeEcc,
536    size: usize,
537    description: Option<DESC>,
538    path: P,
539) -> Result<(), QRCodeError> {
540    to_svg_to_file_inner(generate_qrcode_from_segments(segments, ecc)?, size, description, path)
541}
542
543/// Encode data to a SVG image via a writer.
544#[inline]
545pub fn to_svg_to_writer<D: AsRef<[u8]>, DESC: AsRef<str>, W: Write>(
546    data: D,
547    ecc: QrCodeEcc,
548    size: usize,
549    description: Option<DESC>,
550    writer: &mut W,
551) -> Result<(), QRCodeError> {
552    to_svg_inner(generate_qrcode(data, ecc)?, size, description, writer)
553}
554
555/// Encode text to a SVG image via a writer.
556#[inline]
557pub fn to_svg_to_writer_from_str<S: AsRef<str>, DESC: AsRef<str>, W: Write>(
558    text: S,
559    ecc: QrCodeEcc,
560    size: usize,
561    description: Option<DESC>,
562    writer: &mut W,
563) -> Result<(), QRCodeError> {
564    to_svg_inner(generate_qrcode_from_str(text, ecc)?, size, description, writer)
565}
566
567/// Encode segments to a SVG image via a writer.
568#[inline]
569pub fn to_svg_to_writer_from_segments<DESC: AsRef<str>, W: Write>(
570    segments: &[QrSegment],
571    ecc: QrCodeEcc,
572    size: usize,
573    description: Option<DESC>,
574    writer: &mut W,
575) -> Result<(), QRCodeError> {
576    to_svg_inner(generate_qrcode_from_segments(segments, ecc)?, size, description, writer)
577}
578
579#[cfg(feature = "image")]
580/// Encode data to a PNG image in memory.
581#[inline]
582pub fn to_png_to_vec<D: AsRef<[u8]>>(
583    data: D,
584    ecc: QrCodeEcc,
585    size: usize,
586) -> Result<Vec<u8>, QRCodeError> {
587    to_png_to_vec_inner(generate_qrcode(data, ecc)?, size)
588}
589
590#[cfg(feature = "image")]
591/// Encode text to a PNG image in memory.
592#[inline]
593pub fn to_png_to_vec_from_str<S: AsRef<str>>(
594    text: S,
595    ecc: QrCodeEcc,
596    size: usize,
597) -> Result<Vec<u8>, QRCodeError> {
598    to_png_to_vec_inner(generate_qrcode_from_str(text, ecc)?, size)
599}
600
601#[cfg(feature = "image")]
602/// Encode segments to a PNG image in memory.
603#[inline]
604pub fn to_png_to_vec_from_segments(
605    segments: &[QrSegment],
606    ecc: QrCodeEcc,
607    size: usize,
608) -> Result<Vec<u8>, QRCodeError> {
609    to_png_to_vec_inner(generate_qrcode_from_segments(segments, ecc)?, size)
610}
611
612#[cfg(feature = "image")]
613/// Encode data to a PNG image via a file path.
614#[inline]
615pub fn to_png_to_file<D: AsRef<[u8]>, P: AsRef<Path>>(
616    data: D,
617    ecc: QrCodeEcc,
618    size: usize,
619    path: P,
620) -> Result<(), QRCodeError> {
621    to_png_to_file_inner(generate_qrcode(data, ecc)?, size, path)
622}
623
624#[cfg(feature = "image")]
625/// Encode text to a PNG image via a file path.
626#[inline]
627pub fn to_png_to_file_from_str<S: AsRef<str>, P: AsRef<Path>>(
628    text: S,
629    ecc: QrCodeEcc,
630    size: usize,
631    path: P,
632) -> Result<(), QRCodeError> {
633    to_png_to_file_inner(generate_qrcode_from_str(text, ecc)?, size, path)
634}
635
636#[cfg(feature = "image")]
637/// Encode text to a PNG image via a file path.
638#[inline]
639pub fn to_png_to_file_from_segments<P: AsRef<Path>>(
640    segments: &[QrSegment],
641    ecc: QrCodeEcc,
642    size: usize,
643    path: P,
644) -> Result<(), QRCodeError> {
645    to_png_to_file_inner(generate_qrcode_from_segments(segments, ecc)?, size, path)
646}
647
648#[cfg(feature = "image")]
649/// Encode data to a PNG image via a writer.
650#[inline]
651pub fn to_png_to_writer<D: AsRef<[u8]>, W: Write>(
652    data: D,
653    ecc: QrCodeEcc,
654    size: usize,
655    writer: &mut W,
656) -> Result<(), QRCodeError> {
657    to_png_inner(generate_qrcode(data, ecc)?, size, writer)
658}
659
660#[cfg(feature = "image")]
661/// Encode text to a PNG image via a writer.
662#[inline]
663pub fn to_png_to_writer_from_str<S: AsRef<str>, W: Write>(
664    text: S,
665    ecc: QrCodeEcc,
666    size: usize,
667    writer: &mut W,
668) -> Result<(), QRCodeError> {
669    to_png_inner(generate_qrcode_from_str(text, ecc)?, size, writer)
670}
671
672#[cfg(feature = "image")]
673/// Encode segments to a PNG image via a writer.
674#[inline]
675pub fn to_png_to_writer_from_segments<W: Write>(
676    segments: &[QrSegment],
677    ecc: QrCodeEcc,
678    size: usize,
679    writer: &mut W,
680) -> Result<(), QRCodeError> {
681    to_png_inner(generate_qrcode_from_segments(segments, ecc)?, size, writer)
682}
683
684#[cfg(feature = "image")]
685/// Encode data to a image buffer.
686pub fn to_image_buffer<D: AsRef<[u8]>>(
687    data: D,
688    ecc: QrCodeEcc,
689    size: usize,
690) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
691    to_image_buffer_inner(generate_qrcode(data, ecc)?, size)
692}
693
694#[cfg(feature = "image")]
695/// Encode text to a image buffer.
696pub fn to_image_buffer_from_str<S: AsRef<str>>(
697    text: S,
698    ecc: QrCodeEcc,
699    size: usize,
700) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
701    to_image_buffer_inner(generate_qrcode_from_str(text, ecc)?, size)
702}
703
704#[cfg(feature = "image")]
705/// Encode segments to a image buffer.
706pub fn to_image_buffer_from_segments<S: AsRef<str>>(
707    segments: &[QrSegment],
708    ecc: QrCodeEcc,
709    size: usize,
710) -> Result<ImageBuffer<Luma<u8>, Vec<u8>>, QRCodeError> {
711    to_image_buffer_inner(generate_qrcode_from_segments(segments, ecc)?, size)
712}