1pub 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 }
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 }
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#[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#[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#[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
447pub 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
456pub 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
465pub 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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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")]
685pub 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")]
695pub 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")]
705pub 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}