File Coverage

blib/lib/QRCode/Base45.pm
Criterion Covered Total %
statement 52 52 100.0
branch 12 12 100.0
condition n/a
subroutine 9 9 100.0
pod 2 2 100.0
total 75 75 100.0


line stmt bran cond sub pod time code
1             package QRCode::Base45;
2              
3 2     2   151655 use 5.10.0;
  2         17  
4 2     2   13 use strict;
  2         4  
  2         69  
5 2     2   12 use warnings;
  2         5  
  2         64  
6 2     2   11 use feature 'state';
  2         4  
  2         310  
7 2     2   13 use Carp;
  2         11  
  2         139  
8 2     2   1343 use Encode;
  2         22760  
  2         177  
9 2     2   20 use base qw(Exporter);
  2         4  
  2         1503  
10             our @EXPORT = qw(encode_base45 decode_base45);
11              
12             =head1 NAME
13              
14             QRCode::Base45 - Base45 encoding used in QR codes
15              
16             =cut
17              
18             our $VERSION = '0.03';
19              
20             our $ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
21             # 0 1 2 3 4
22             # 012345678901234567890123456789012345678901234
23              
24             =head1 SYNOPSIS
25              
26             use QRCode::Base45;
27              
28             my $text_for_qrcode = encode_base45($binary_or_utf8_data);
29             my $binary_data = decode_base45($text_from_qrcode);
30              
31             =head1 DESCRIPTION
32              
33             This module handles encoding/decoding of Base45 data,
34             as described in
35             L.
36             Base45 is used especially in QR codes, for example in European vaccination
37             certificates.
38              
39             =head2 encode_base45
40              
41             Takes an arbitrary string as argument, and returns the Base45 representation
42             of it. Character strings (as opposed to byte strings) are encoded to bytes
43             as UTF-8.
44              
45             For zero-length input strings (undef or '') an empty string ('') is returned.
46              
47             =cut
48              
49             sub encode_base45 {
50 56     56 1 29738 my ($input) = @_;
51              
52 56 100       180 return '' if !length $input;
53              
54 54 100       151 $input = Encode::encode('UTF-8', $input)
55             if utf8::is_utf8($input);
56              
57 54         441 my $output = '';
58              
59 54         298 for my $chunk ($input =~ /..?/msg) {
60 86         125 my $sum = 0;
61 86         231 for my $byte (unpack('C*', $chunk)) {
62 121         173 $sum *= 256;
63 121         173 $sum += $byte;
64             }
65 86         190 for (0 .. length($chunk)) {
66 207         347 $output .= substr($ALPHABET, $sum % 45, 1);
67 207         411 $sum = int($sum/45);
68             }
69             }
70              
71 54         260 return $output;
72             }
73              
74             =head2 decode_base45
75              
76             Takes a textual Base45 representation of data, and tries to decode it.
77             Returned value is a byte string (as this function cannot possibly know
78             whether the content should be interpreted as bytes or UTF-8).
79             The caller has to decode the returned byte string to characters afterwards,
80             if needed.
81              
82             For zero-length input strings (undef or '') an empty string ('') is returned.
83              
84             For invalid inputs, such as strings of length 3n+1 or characters
85             outside of the Base45 alphabet, this function croak()s.
86              
87             =cut
88              
89             sub decode_base45 {
90 68     68 1 8924 my ($input) = @_;
91              
92 68 100       187 return '' if !length $input;
93              
94 66 100       786 croak "decode_base45(): invalid input length " . length($input)
95             . " is 3*n+1"
96             if length($input) % 3 == 1;
97              
98 61         106 my $map_count = 0;
99 61         105 state $value_of = { map { $_ => $map_count++ } split //, $ALPHABET };
  45         90  
100              
101 61         112 my $output = '';
102 61         384 for my $chunk ($input =~ /(...?)/g) {
103 96         142 my $sum = 0;
104 96         262 for my $c (reverse map { $value_of->{$_} } split //, $chunk) {
  232         457  
105 224 100       1054 croak "decode_base45(): chunk <$chunk> contains invalid character(s)"
106             if !defined $c;
107 217         271 $sum *= 45;
108 217         301 $sum += $c;
109             }
110              
111 89 100       249 if (length $chunk == 3) {
112 38         88 $output .= pack('C', $sum >> 8);
113 38         57 $sum &= 0x00FF;
114             }
115 89         250 $output .= pack('C', $sum);
116             }
117              
118 54         258 return $output;
119             }
120              
121             =head1 AUTHOR
122              
123             Jan "Yenya" Kasprzak, C<< >>
124              
125             =head1 BUGS
126              
127             Please report any bugs or feature requests to C, or through
128             the web interface at L. I will be notified, and then you'll
129             automatically be notified of progress on your bug as I make changes.
130              
131             The Base45 encoding is relatively new. After it is standardized
132             and maybe used elsewhere apart from QR codes,
133             this module should probably be moved to some other namespace,
134             such as IETF:: or RFCxyzq::.
135              
136             =head1 INSTALLATION
137              
138             To install this module, run the following commands:
139              
140             perl Makefile.PL
141             make
142             make test
143             make install
144              
145             =head1 SUPPORT
146              
147             You can find documentation for this module with the perldoc command.
148              
149             perldoc QRCode::Base45
150              
151              
152             You can also look for information at:
153              
154             =over 4
155              
156             =item * Github repository
157              
158             L
159              
160             =item * RT: CPAN's request tracker (report bugs here)
161              
162             L
163              
164             =item * CPAN Ratings
165              
166             L
167              
168             =item * Search CPAN
169              
170             L
171              
172             =back
173              
174              
175             =head1 ACKNOWLEDGEMENTS
176              
177              
178             =head1 LICENSE AND COPYRIGHT
179              
180             This software is copyright (c) 2021 by Jan "Yenya" Kasprzak.
181              
182             This is free software; you can redistribute it and/or modify it under
183             the same terms as the Perl 5 programming language system itself.
184              
185              
186             =cut
187              
188             1; # End of QRCode::Base45