-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathqrencode.el
1101 lines (999 loc) · 43.5 KB
/
qrencode.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; qrencode.el --- QRCode encoder -*- lexical-binding: t -*-
;; Copyright (C) 2021-2024 Rüdiger Sonderfeld <[email protected]>
;; Author: Rüdiger Sonderfeld <[email protected]>
;; Keywords: qrcode comm
;; Version: 1.3-beta2
;; Package-Requires: ((emacs "25.1"))
;; Package: qrencode
;; URL: https://github.com/ruediger/qrencode-el
;; This file is NOT part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; qrencode provides a QR Code (ISO/IEC 18004:2015) encoder written
;; entirely in Emacs Lisp (elisp). See README.org for full
;; documentation.
;;
;; Usage: Call `qrencode-region' to convert a region into a QR Code
;; and `qrencode-url-at-point' to convert a URL. QR Codes are shown
;; in a separate buffer.
;;; Code:
(require 'cl-lib)
(eval-when-compile (require 'easymenu))
(require 'seq)
(require 'thingatpt)
;;; Error correction
;; Reed solomon ECC implementation based on https://research.swtch.com/field
(defun qrencode--mul-no-lut (x y poly)
"Caryless-multiply X and Y modulo POLY."
(let ((z 0))
(while (> x 0)
(when (/= (logand x 1) 0)
(setq z (logxor z y)))
(setq x (ash x -1))
(setq y (ash y +1))
;; Modulo operation: If we shift y too far we have to subtract
;; poly again.
(when (/= (logand y #x100) 0)
(setq y (logxor y poly))))
z))
(defun qrencode--init-field (poly a)
"Initialise a field LUT with POLY and A."
(let ((log (make-vector 256 0))
(exp (make-vector 510 0))
(x 1))
(dotimes (i 255)
(aset exp i x)
(aset exp (+ i 255) x)
(aset log x i)
(setq x (qrencode--mul-no-lut x a poly)))
(aset log 0 255)
(list log exp)))
(defun qrencode--field-exp (field e)
"Return exponential E in FIELD."
(if (< e 0)
0
(aref (cadr field) (% e 255))))
(defun qrencode--field-log (field l)
"Return log of L in FIELD."
(if (= l 0)
-1
(aref (car field) l)))
(defun qrencode--field-mul (field x y)
"Multiply X with Y in FIELD."
(if (or (= x 0) (= y 0))
0
(qrencode--field-exp field
(+ (qrencode--field-log field x)
(qrencode--field-log field y)))))
(defun qrencode--gen (field e)
"Return generator polynomial of degree E and its log in FIELD."
(let ((p (make-vector (1+ e) 0))
(lp (make-vector (1+ e) 0))) ; log(p)
;; calculate p
(aset p e 1)
(dotimes (i e)
(let ((c (qrencode--field-exp field i)))
(dotimes (j e)
(aset p j (logxor
(qrencode--field-mul field (aref p j) c)
(aref p (1+ j)))))
(aset p e (qrencode--field-mul field (aref p e) c))))
;; calculate log(p)
(dotimes (i (1+ e))
(let ((c (aref p i)))
(if (= c 0)
(aset lp i 255)
(aset lp i (qrencode--field-log field c)))))
(list p (seq-subseq lp 1))))
(defun qrencode--ecc (data c &optional field lgen)
"Return ECC for DATA with length C.
Optionally provide a FIELD and LGEN (log of generator polynomial)."
(setq field (or field (qrencode--init-field #x11d 2)))
(setq lgen (or lgen (cadr (qrencode--gen field c))))
(let ((p (vconcat data (make-vector c 0)))) ; Data padded with 0 bytes
(dotimes (i (length data))
(unless (= (aref p i) 0)
(let ((exp (qrencode--field-log field (aref p i))))
(dotimes (j (length lgen))
(let ((lg (aref lgen j)))
(when (/= lg 255)
(aset p (+ i j 1)
(logxor (aref p (+ i j 1))
(aref (cadr field) (+ exp lg))))))))))
(seq-subseq p (length data))))
;;; Util
(defun qrencode--size (version)
"Return number of modules for VERSION."
(cl-assert (<= 1 version 40) 'show-args "Version %d out of valid range [1, 40]" version)
(+ (* (1- version) 4) 21))
;;; Data encoding
(defun qrencode--mode (mode)
"Return encoding representation of MODE."
(pcase mode
('byte 4) ; 0100
;; TODO(#11): Support other encodings
(other (error "Mode %s not supported" other))))
(defun qrencode--encode-byte (input version)
"Return INPUT encoded in byte format for QR Code size VERSION.
See Section 7.4 of ISO/IEC standard. This code adds a 4 bit mode
indicator and then the character count in either 16 bit (for
version > 9) or 8 bit, followed by input."
(let* ((l (length input))
(rest (logand l #xF)))
(cl-assert (<= l (if (> version 9) 65535 255)))
(vconcat
(vector (logior (ash (qrencode--mode 'byte) 4)
;; Version <=9 use 8 bit, larger 16 bit for size
(ash l (if (<= version 9) -4 -12))))
(when (> version 9)
(vector (ash (logand l #xFF0) -4)))
(cl-loop for d across input
vconcat (vector (logior (ash rest 4) (ash d -4)))
do (setq rest (logand d #xF)))
(vector (ash rest 4)))))
;;; Basic patterns and templates handling
(defconst qrencode--finder-pattern
'[[1 1 1 1 1 1 1]
[1 0 0 0 0 0 1]
[1 0 1 1 1 0 1]
[1 0 1 1 1 0 1]
[1 0 1 1 1 0 1]
[1 0 0 0 0 0 1]
[1 1 1 1 1 1 1]]
"QRCode Finder Pattern.")
(defconst qrencode--alignment-pattern
'[[1 1 1 1 1]
[1 0 0 0 1]
[1 0 1 0 1]
[1 0 0 0 1]
[1 1 1 1 1]]
"QRCode Alignment Pattern.")
(defconst qrencode--alignment-pattern-placement
'[nil
nil
[6 18]
[6 22]
[6 26]
[6 30]
[6 34]
[6 22 38]
[6 24 42]
[6 26 46]
[6 28 50]
[6 30 54]
[6 32 58]
[6 34 62]
[6 26 46 66]
[6 26 48 70]
[6 26 50 74]
[6 30 54 78]
[6 30 56 82]
[6 30 58 86]
[6 34 62 90]
[6 28 50 72 94]
[6 26 50 74 98]
[6 30 54 78 102]
[6 28 54 80 106]
[6 32 58 84 110]
[6 30 58 86 114]
[6 34 62 90 118]
[6 26 50 74 98 122]
[6 30 54 78 102 126]
[6 26 52 78 104 130]
[6 30 56 82 108 134]
[6 34 60 86 112 138]
[6 30 58 86 114 142]
[6 34 62 90 118 146]
[6 30 54 78 102 126 150]
[6 24 50 76 102 128 154]
[6 28 54 80 106 132 158]
[6 32 58 84 110 136 162]
[6 26 54 82 110 138 166]
[6 30 58 86 114 142 170]]
"Placement of alignment pattern. Vector index is the version.")
(defun qrencode--square (n &optional init)
"Return a square of size N.
The square is initialised with INIT or 0."
(let ((sq (make-vector n '[])))
(dotimes (i n)
(aset sq i (make-vector n (or init 0))))
sq))
(defun qrencode--aaset (dst x y val)
"Set in sequence of sequences DST position X (inner), Y (outer) to VAL."
(aset (aref dst y) x val))
(defun qrencode--aaref (src x y)
"Return from sequence of sequences SRC value at position X (inner), Y (outer)."
(aref (aref src y) x))
(defun qrencode--copy-square (dst pattern x y)
"Copy to DST from PATTERN at X and Y."
(dotimes (i (length pattern))
(dotimes (j (length (aref pattern i)))
(qrencode--aaset dst (+ x j) (+ y i) (qrencode--aaref pattern j i)))))
(defun qrencode--set-rect (dst x y width height &optional value)
"Set a rectangle in DST at X, Y of WIDTH and HEIGHT to VALUE."
(dotimes (i height)
(dotimes (j width)
(qrencode--aaset dst (+ x j) (+ y i) (or value 1)))))
(defun qrencode--template (version)
"Return basic QRCode template for VERSION."
(let* ((size (qrencode--size version))
(qrcode (qrencode--square size))
(function-pattern (qrencode--square size))) ; Keeps track of location of function patterns
;; Finder pattern
;; Top Left
(qrencode--copy-square qrcode qrencode--finder-pattern 0 0)
(qrencode--set-rect function-pattern 0 0 9 9) ; finder pattern + separator
;; Top Right
(qrencode--copy-square qrcode qrencode--finder-pattern (- size 7) 0)
(qrencode--set-rect function-pattern (- size 8) 0 8 8) ; finder pattern + separator
(when (>= version 7)
(qrencode--set-rect function-pattern (- size 11) 0 3 6)) ; version info
(qrencode--set-rect function-pattern (- size 8) 8 8 1) ; format info
;; Bottom Left
(qrencode--copy-square qrcode qrencode--finder-pattern 0 (- size 7))
(qrencode--set-rect function-pattern 0 (- size 8) 8 8) ; finder pattern + separator
(when (>= version 7)
(qrencode--set-rect function-pattern 0 (- size 11) 6 3)) ; version info
(qrencode--set-rect function-pattern 8 (- size 8) 1 8) ; format info
;; Alignment patterns
(let ((alignment-pattern (aref qrencode--alignment-pattern-placement version)))
(when alignment-pattern
;; Alignment patterns are placed centred at all row/column
;; combinations.
(seq-doseq (r alignment-pattern)
(seq-doseq (c alignment-pattern)
(unless (= 1 (qrencode--aaref function-pattern c r))
(qrencode--copy-square qrcode qrencode--alignment-pattern (- c 2) (- r 2))
(qrencode--set-rect function-pattern (- c 2) (- r 2) 5 5))))))
;; Timing pattern
(cl-loop for i from 8 to (- size 8)
do (qrencode--aaset qrcode 6 i (% (1+ i) 2))
do (qrencode--aaset function-pattern 6 i 1)
do (qrencode--aaset qrcode i 6 (% (1+ i) 2))
do (qrencode--aaset function-pattern i 6 1))
(cons qrcode function-pattern)))
(defun qrencode--nextpos (row column size up)
"Return next possible ROW, COLUMN and UP value in square of SIZE."
;; A QRcode "column" (not to be confused with the column var here,
;; which is the x module position) is two modules (pixels).
;; Placement is from right to left and then upwards.
;;
;; Size or the QRCode is odd. So if a column number is even (except
;; if we are in column < 6 due to the timing pattern in col 6) the
;; go left. Otherwise go upwards/downwards (depending on up) and
;; right. Except if we hit the top/bottom then move a QCode column
;; (two modules) to the right and change direction (up).
(when (= column 6) ; Handle timing pattern column 6
(setq column 5))
(if (= (% column 2) (if (< column 6) 1 0))
(if (<= column 0)
(error "Overflowing pattern. Data too large?")
(list row (1- column) up))
(if up
(if (= row 0)
(if (= column 0)
(error "Overflowing pattern. Data too large?")
(list row (1- column) nil))
(list (1- row) (1+ column) up))
(if (>= row (1- size))
(if (= column 0)
(error "Overflowing pattern. Data too large?")
(list row (1- column) t))
(list (1+ row) (1+ column) up)))))
(defun qrencode--draw-data (qrcode function-pattern version data)
"Draw DATA on QRCODE of VERSION and respecting FUNCTION-PATTERN."
(let* ((size (qrencode--size version))
(up t)
(row (1- size))
(column (1- size)))
(cl-loop for byte across data and didx from 0
do (cl-loop for bidx from 7 downto 0
do (qrencode--aaset qrcode column row (logand (ash byte (- bidx)) 1))
do (unless (and (= didx (1- (length data))) (= bidx 0))
;; Find new position avoiding function-patterns.
(cl-loop do (pcase-let ((`(,nrow ,ncolumn ,nup) (qrencode--nextpos row column size up)))
(setq row nrow
column ncolumn
up nup))
while (= (qrencode--aaref function-pattern row column) 1)))))))
;;; Data masking
(defun qrencode--penalty (qr)
"Return penalty (the higher the worse) for a given QR pattern."
(let ((size (length qr))
(penalty 0))
;; 1. Adjacency:
;; More than 5 adjacent modules of same colour.
(let ((N1 3))
;; Scan columns
(let ((row 0) (col 0))
(while (< row (1- size))
(while (< col (1- size))
(if (= (qrencode--aaref qr col row)
(qrencode--aaref qr (1+ col) row))
(let ((i 1))
(while (and (< col (1- size)) (= (qrencode--aaref qr col row)
(qrencode--aaref qr (1+ col) row)))
(setq i (1+ i)
col (1+ col)))
(when (> i 5)
(setq penalty (+ penalty N1 (- i 5)))))
(setq col (1+ col))))
(setq row (1+ row)
col 0)))
;; Scan rows
(let ((row 0) (col 0))
(while (< col (1- size))
(while (< row (1- size))
(if (= (qrencode--aaref qr col row)
(qrencode--aaref qr col (1+ row)))
(let ((i 1))
(while (and (< row (1- size)) (= (qrencode--aaref qr col row)
(qrencode--aaref qr col (1+ row))))
(setq i (1+ i)
row (1+ row)))
(when (> i 5)
(setq penalty (+ penalty N1 (- i 5)))))
(setq row (1+ row))))
(setq col (1+ col)
row 0))))
;; 2. Block (2×2 block) of modules of the same colour
(let ((N2 3))
(setq penalty (+ penalty
(cl-loop for row from 0 below (1- size)
sum (cl-loop for col from 0 below (1- size)
when (= (qrencode--aaref qr col row)
(qrencode--aaref qr col (1+ row))
(qrencode--aaref qr (1+ col) row)
(qrencode--aaref qr (1+ col) (1+ row)))
sum N2)))))
;; 3. 1:1:3:1:1 pattern
;; Pattern: 4 light modules before/after 1011101. I.e., 00001011101 or 10111010000.
(let ((N3 40))
(let ((row 0) (col 0))
(while (< row (1- size))
(while (< col (- size 10))
;; Optimisation: Both patterns match in these spots
(when (and (= 0 (qrencode--aaref qr (+ col 1) row))
(= 1 (qrencode--aaref qr (+ col 4) row))
(= 0 (qrencode--aaref qr (+ col 5) row))
(= 1 (qrencode--aaref qr (+ col 6) row))
(= 0 (qrencode--aaref qr (+ col 9) row))
(or (and
;; Pattern beginning with 0
(= 0 (qrencode--aaref qr col row))
;; 0
(= 0 (qrencode--aaref qr (+ col 2) row))
(= 0 (qrencode--aaref qr (+ col 3) row))
;; 1
(= 1 (qrencode--aaref qr (+ col 7) row))
(= 1 (qrencode--aaref qr (+ col 8) row))
;; 0
(= 1 (qrencode--aaref qr (+ col 10) row)))
(and
;; Pattern ending with 0
(= 1 (qrencode--aaref qr col row))
;; 0
(= 1 (qrencode--aaref qr (+ col 2) row))
(= 1 (qrencode--aaref qr (+ col 3) row))
;; 1
(= 0 (qrencode--aaref qr (+ col 7) row))
(= 0 (qrencode--aaref qr (+ col 8) row))
;; 0
(= 0(qrencode--aaref qr (+ col 10) row)))))
(setq penalty (+ penalty N3)))
(setq col (1+ col)))
(setq row (1+ row)
col 0)))
(let ((row 0) (col 0))
(while (< col (1- size))
(while (< row (- size 10))
;; Optimisation: Both patterns match in these spots
(when (and (= 0 (qrencode--aaref qr col (+ row 1)))
(= 1 (qrencode--aaref qr col (+ row 4)))
(= 0 (qrencode--aaref qr col (+ row 5)))
(= 1 (qrencode--aaref qr col (+ row 6)))
(= 0 (qrencode--aaref qr col (+ row 9)))
(or (and
;; Pattern beginning with 0
(= 0 (qrencode--aaref qr col row))
;; 0
(= 0 (qrencode--aaref qr col (+ row 2)))
(= 0 (qrencode--aaref qr col (+ row 3)))
;; 1
(= 1 (qrencode--aaref qr col (+ row 7)))
(= 1 (qrencode--aaref qr col (+ row 8)))
;; 0
(= 1 (qrencode--aaref qr col (+ row 10))))
(and
;; Pattern ending with 0
(= 1 (qrencode--aaref qr col row))
;; 0
(= 1 (qrencode--aaref qr col (+ row 2)))
(= 1 (qrencode--aaref qr col (+ row 3)))
;; 1
(= 0 (qrencode--aaref qr col (+ row 7)))
(= 0 (qrencode--aaref qr col (+ row 8)))
;; 0
(= 0(qrencode--aaref qr col (+ row 10))))))
(setq penalty (+ penalty N3)))
(setq row (1+ row)))
(setq col (1+ col)
row 0))))
;; 4. Ratio of dark to light
(let ((N4 10)
(dark (cl-loop for row across qr
sum (cl-loop for d across row sum d))))
(setq penalty
(+ penalty
;; Every 5% deviation from 50% dark/white ratio is penalised.
(* (floor (/ (abs (- 0.5 (/ dark (* size size)))) 0.05) N4)))))
penalty))
(defconst qrencode--masks
[(lambda (i j) (= (% (+ i j) 2) 0))
(lambda (i _j) (= (% i 2) 0))
(lambda (_i j) (= (% j 3) 0))
(lambda (i j) (= (% (+ i j) 3) 0))
(lambda (i j) (= (% (+ (/ i 2) (/ j 3)) 2) 0))
(lambda (i j) (= (+ (% (* i j) 2) (% (* i j) 3)) 0))
(lambda (i j) (= (% (+ (% (* i j) 2) (% (* i j) 3)) 2) 0))
(lambda (i j) (= (% (+ (% (+ i j) 2) (% (* i j) 3)) 2) 0))]
"Mask patterns.")
(defun qrencode--copy (seq)
"Copy a sequence of sequence SEQ."
(let ((sq (make-vector (length seq) '[])))
(dotimes (i (length seq))
(aset sq i (seq-copy (aref seq i))))
sq))
(defun qrencode--apply-mask (qrcode function-pattern datamask)
"Return a copy of QRCODE with DATAMASK applied except for FUNCTION-PATTERN."
(let ((qr (qrencode--copy qrcode))
(size (length qrcode))
(m (aref qrencode--masks datamask)))
(cl-loop for i below size
do (cl-loop for j below size
unless (= (qrencode--aaref function-pattern j i) 1)
do (qrencode--aaset qr j i
(logxor (qrencode--aaref qr j i)
(if (funcall m i j) 1 0)))))
qr))
(defun qrencode--find-best-mask (qr function-pattern)
"Return QR with best mask applied and mask number, avoiding FUNCTION-PATTERN."
(let (bestqr (bestmask 0) (bestpenalty #xFFFFFFFF))
(dotimes (mask (length qrencode--masks))
(let* ((newqr (qrencode--apply-mask qr function-pattern mask))
(penalty (qrencode--penalty newqr)))
(when (< penalty bestpenalty)
(setq bestqr newqr
bestmask mask
bestpenalty penalty))))
(cons bestqr bestmask)))
;;; Version/Info encoding
(defun qrencode--bit-length (x)
"Return the number of bits necessary to represent integer X in binary."
(let ((r 0))
(while (> x 0)
(setq x (ash x -1)
r (1+ r)))
r))
(defun qrencode--mod (x y)
"Return remainder of carry-less long division of X over Y."
(let ((xbl (qrencode--bit-length x))
(ybl (qrencode--bit-length y)))
(unless (< xbl ybl)
(cl-loop for i from (- xbl ybl) downto 0
when (/= (logand x (ash 1 (+ i ybl -1))) 0)
do (setq x (logxor x (ash y i)))))
x))
(defun qrencode--bch-encode (data &optional mask)
"Return DATA properly error correction encoded for info data.
Optionally provide a MASK or #x5412 is used."
(logxor (ash data 10) (qrencode--mod (ash data 10)
#x537) ; 10100110111
(or mask #x5412))) ; 101010000010010
(defun qrencode--errcorr (errcorr)
"Return info representation of error correction level ERRCORR."
(pcase errcorr
('L 1) ; 01
('M 0) ; 00
('Q 3) ; 11
('H 2) ; 10
(other (error "Unknown error correction level %s" other))))
(defun qrencode--encode-info (qr errcorr datamask)
"Set info data on QR encoding ERRCORR and DATAMASK."
(let* ((size (length qr))
(info (qrencode--bch-encode (logior (ash (qrencode--errcorr errcorr) 3) datamask)))
(r1 0) (c1 8)
(r2 8) (c2 (1- size)))
(dotimes (i 8)
(let ((data (logand (ash info (- i)) 1)))
(qrencode--aaset qr c1 r1 data)
(setq r1 (1+ r1))
(when (= r1 6) ; Avoid timing pattern
(setq r1 (1+ r1)))
(qrencode--aaset qr c2 r2 data)
(setq c2 (1- c2))))
(setq c1 (1- c1)
r1 (1- r1)
r2 (- size 7)
c2 8)
(qrencode--aaset qr c2 (1- r2) 1) ; Dark module in corner
(cl-loop for i from 8 to 14
do (let ((data (logand (ash info (- i)) 1)))
(qrencode--aaset qr c1 r1 data)
(setq c1 (1- c1))
(when (= c1 6) ; Avoid timing pattern
(setq c1 (1- c1)))
(qrencode--aaset qr c2 r2 data)
(setq r2 (1+ r2))))))
(defun qrencode--version-ecc (v)
"Return version V info error correction code."
(logior (ash v 12)
(qrencode--mod (ash v 12)
#x1F25))) ; 1111100100101
(defun qrencode--encode-version (qr version)
"Set on QR the VERSION data."
(unless (< version 7) ; only version >= 7 have version encoding
(let ((e (qrencode--version-ecc version))
(size (length qr)))
(let ((c1 0) (r1 (- size 11)) ; Bottom left version location
(c2 (- size 11)) (r2 0)) ; Top right version location
(cl-loop for i from 0 to 17
;; Calculate row/column. A and B usage as row/column
;; switch depend on bottom/top location.
do (let ((a (/ i 3)) (b (% i 3))
(val (logand (ash e (- i)) 1)))
(qrencode--aaset qr (+ c1 a) (+ r1 b) val)
(qrencode--aaset qr (+ c2 b) (+ r2 a) val)))))))
(defun qrencode--unused (_)
"This doesn't use _ but tricks the compiler.")
;; Analyse data: sizing etc.
(defun qrencode--char-count-bits (version mode)
"Return the number of bits per character given VERSION and MODE."
(cdr (assq mode
(pcase version
((and n (guard (<= 1 n 9)))
(qrencode--unused n)
'((numeric . 10)
(alphanumeric . 9)
(byte . 8)
(kanji . 8)))
((and n (guard (<= 10 n 26)))
(qrencode--unused n)
'((numeric . 12)
(alphanumeric . 11)
(byte . 16)
(kanji . 10)))
((and n (guard (<= 27 n 40)))
(qrencode--unused n)
'((numeric . 14)
(alphanumeric . 13)
(byte . 16)
(kanji . 12)))
(other (error "Unsupported version %d (range 1 to 40)" other))))))
(defun qrencode--length-in-version (n version mode)
"Return length of a string of size N in VERSION and MODE."
(+ n (ceiling (+ 4 (/ (qrencode--char-count-bits version mode) 8)))))
(defconst qrencode--size-table
[(26 . ((L . ( 7 3 1))
(M . (10 2 1))
(Q . (13 1 1))
(H . (17 1 1))))
(44 . ((L . (10 2 1))
(M . (16 0 1))
(Q . (22 0 1))
(H . (28 0 1))))
(70 . ((L . (15 1 1))
(M . (26 0 1))
(Q . (36 0 2))
(H . (44 0 2))))
(100 . ((L . (20 0 1))
(M . (36 0 2))
(Q . (52 0 2))
(H . (64 0 4))))
(134 . ((L . (26 0 1)) ; 5
(M . (48 0 2))
(Q . (72 0 ((2 . 15) . (2 . 16))))
(H . (88 0 ((2 . 11) . (2 . 12))))))
(172 . ((L . (36 0 2))
(M . (64 0 4))
(Q . (96 0 4))
(H . (112 0 4))))
(196 . ((L . (40 0 2))
(M . (72 0 4))
(Q . (108 0 ((2 . 14) . (4 . 15))))
(H . (130 0 ((4 . 13) . (1 . 14))))))
(242 . ((L . (48 0 2))
(M . (88 0 ((2 . 38) . (2 . 39))))
(Q . (132 0 ((4 . 18) . (2 . 19))))
(H . (156 0 ((4 . 14) . (2 . 15))))))
(292 . ((L . (60 0 2))
(M . (110 0 ((3 . 36) . (2 . 37))))
(Q . (160 0 ((4 . 16) . (4 . 17))))
(H . (192 0 ((4 . 12) . (4 . 13))))))
(346 . ((L . (72 0 ((2 . 68) . (2 . 69))))
(M . (130 0 ((4 . 43) . (1 . 44))))
(Q . (192 0 ((6 . 19) . (2 . 20))))
(H . (224 0 ((6 . 15) . (2 . 16))))))
(404 . ((L . (80 0 4))
(M . (150 0 ((1 . 50) . (4 . 51))))
(Q . (224 0 ((4 . 22) . (4 . 23))))
(H . (264 0 ((3 . 12) . (8 . 13))))))
(466 . ((L . (96 0 ((2 . 92) . (2 . 93))))
(M . (176 0 ((6 . 36) . (2 . 37))))
(Q . (260 0 ((4 . 20) . (6 . 21))))
(H . (308 0 ((7 . 14) . (4 . 15))))))
(532 . ((L . (104 0 4))
(M . (198 0 ((8 . 37) . (1 . 38))))
(Q . (288 0 ((8 . 20) . (4 . 21))))
(H . (352 0 ((12 . 11) . (4 . 12))))))
(581 . ((L . (120 0 ((3 . 115) . (1 . 116))))
(M . (216 0 ((4 . 40) . (5 . 41))))
(Q . (320 0 ((11 . 16) . (5 . 17))))
(H . (384 0 ((11 . 12) . (5 . 13))))))
(655 . ((L . (132 0 ((5 . 87) . (1 . 88))))
(M . (240 0 ((5 . 41) . (5 . 42))))
(Q . (360 0 ((5 . 24) . (7 . 25))))
(H . (432 0 ((11 . 12) . (7 . 13))))))
(733 . ((L . (144 0 ((5 . 98) . (1 . 99))))
(M . (280 0 ((7 . 45) . (3 . 46))))
(Q . (408 0 ((15 . 19) . (2 . 20))))
(H . (480 0 ((3 . 15) . (13 . 16))))))
(815 . ((L . (168 0 ((1 . 107) . (5 . 108))))
(M . (308 0 ((10 . 46) . (1 . 47))))
(Q . (448 0 ((1 . 22) . (15 . 23))))
(H . (532 0 ((2 . 14) . (17 . 15))))))
(901 . ((L . (180 0 ((5 . 120) . (1 . 121))))
(M . (338 0 ((9 . 43) . (4 . 44))))
(Q . (504 0 ((17 . 22) . (1 . 23))))
(H . (588 0 ((2 . 14) . (19 . 15))))))
(991 . ((L . (196 0 ((3 . 113) . (4 . 114))))
(M . (364 0 ((3 . 44) . (11 . 45))))
(Q . (546 0 ((17 . 21) . (4 . 22))))
(H . (650 0 ((9 . 13) . (16 . 14))))))
(1085 . ((L . (224 0 ((3 . 107) . (5 . 108))))
(M . (416 0 ((3 . 41) . (13 . 42))))
(Q . (600 0 ((15 . 24) . (5 . 25))))
(H . (700 0 ((15 . 15) . (10 . 16))))))
(1156 . ((L . (224 0 ((4 . 116) . (4 . 117))))
(M . (442 0 17))
(Q . (644 0 ((17 . 22) . (6 . 23))))
(H . (750 0 ((19 . 16) . (6 . 17))))))
(1258 . ((L . (252 0 ((2 . 111) . (7 . 112))))
(M . (476 0 17))
(Q . (690 0 ((7 . 24) . (16 . 25))))
(H . (816 0 34))))
(1364 . ((L . (270 0 ((4 . 121) . (5 . 122))))
(M . (504 0 ((4 . 47) . (14 . 48))))
(Q . (750 0 ((11 . 24) . (14 . 25))))
(H . (900 0 ((16 . 15) . (14 . 16))))))
(1474 . ((L . (300 0 ((6 . 117) . (4 . 118))))
(M . (560 0 ((6 . 45) . (14 . 46))))
(Q . (810 0 ((11 . 24) . (16 . 25))))
(H . (960 0 ((30 . 16) . (2 . 17))))))
(1588 . ((L . (312 0 ((8 . 106) . (4 . 107))))
(M . (588 0 ((8 . 47) . (13 . 48))))
(Q . (870 0 ((7 . 24) . (22 . 25))))
(H . (1050 0 ((22 . 15) . (13 . 16))))))
(1706 . ((L . (336 0 ((10 . 114) . (2 . 115))))
(M . (644 0 ((19 . 46) . (4 . 47))))
(Q . (952 0 ((28 . 22) . (6 . 23))))
(H . (1110 0 ((33 . 16) . (4 . 17))))))
(1828 . ((L . (360 0 ((8 . 122) . (4 . 123))))
(M . (700 0 ((22 . 45) . (3 . 46))))
(Q . (1020 0 ((8 . 23) . (26 . 24))))
(H . (1200 0 ((12 . 15) . (28 . 16))))))
(1921 . ((L . (390 0 ((3 . 117) . (10 . 118))))
(M . (728 0 ((3 . 45) . (23 . 46))))
(Q . (1050 0 ((4 . 24) . (31 . 25))))
(H . (1260 0 ((11 . 15) . (31 . 16))))))
(2051 . ((L . (420 0 ((7 . 116) . (7 . 117))))
(M . (784 0 ((21 . 45) . (7 . 46))))
(Q . (1140 0 ((1 . 23) . (37 . 24))))
(H . (1350 0 ((19 . 15) . (26 . 16))))))
(2185 . ((L . (450 0 ((5 . 115) . (10 . 116))))
(M . (812 0 ((19 . 47) . (10 . 48))))
(Q . (1200 0 ((15 . 24) . (25 . 25))))
(H . (1440 0 ((23 . 15) . (25 . 16))))))
(2323 . ((L . (480 0 ((13 . 115) . (3 . 116))))
(M . (868 0 ((2 . 46) . (29 . 47))))
(Q . (1290 0 ((42 . 24) . (1 . 25))))
(H . (1530 0 ((23 . 15) . (28 . 16))))))
(2465 . ((L . (510 0 17))
(M . (924 0 ((10 . 46) . (23 . 47))))
(Q . (1350 0 ((10 . 24) . (35 . 25))))
(H . (1620 0 ((19 . 15) . (35 . 16))))))
(2611 . ((L . (540 0 ((17 . 115) . (1 . 116))))
(M . (980 0 ((14 . 46) . (21 . 47))))
(Q . (1440 0 ((29 . 24) . (19 . 25))))
(H . (1710 0 ((11 . 15) . (46 . 16))))))
(2761 . ((L . (570 0 ((13 . 115) . (6 . 116))))
(M . (1036 0 ((14 . 46) . (23 . 47))))
(Q . (1530 0 ((44 . 24) . (7 . 25))))
(H . (1800 0 ((59 . 16) . (1 . 17))))))
(2876 . ((L . (570 0 ((12 . 121) . (7 . 122))))
(M . (1064 0 ((12 . 47) . (26 . 48))))
(Q . (1590 0 ((39 . 24) . (14 . 25))))
(H . (1890 0 ((22 . 15) . (41 . 16))))))
(3034 . ((L . (600 0 ((6 . 121) . (14 . 122))))
(M . (1120 0 ((6 . 47) . (34 . 48))))
(Q . (1680 0 ((46 . 24) . (10 . 25))))
(H . (1980 0 ((2 . 15) . (64 . 16))))))
(3196 . ((L . (630 0 ((17 . 122) . (4 . 123))))
(M . (1204 0 ((29 . 46) . (14 . 47))))
(Q . (1770 0 ((49 . 24) . (10 . 25))))
(H . (2100 0 ((24 . 15) . (46 . 16))))))
(3362 . ((L . (660 0 ((4 . 122) . (18 . 123))))
(M . (1260 0 ((13 . 46) . (32 . 47))))
(Q . (1860 0 ((48 . 24) . (14 . 25))))
(H . (2220 0 ((42 . 15) . (32 . 16))))))
(3532 . ((L . (720 0 ((20 . 117) . (4 . 118))))
(M . (1316 0 ((40 . 47) . (7 . 48))))
(Q . (1950 0 ((43 . 24) . (22 . 25))))
(H . (2310 0 ((10 . 15) . (67 . 16))))))
(3706 . ((L . (750 0 ((19 . 118) . (6 . 119))))
(M . (1372 0 ((18 . 47) . (31 . 48))))
(Q . (2040 0 ((34 . 24) . (34 . 25))))
(H . (2430 0 ((20 . 15) . (61 . 16))))))]
"List of size table.
Index is version number - 1 and content is cons of size to an
assoc list of error correction level to number of error
correction code words, p, and error correction blocks. See Table
9 in ISO/IEC 18004:2015.")
(defun qrencode--find-version (n mode &optional errcorr)
"Return cons of version and error correction based on data length N, MODE.
Provide ERRCORR if a specific error correction level is desired,
otherwise this will try to find the highest level in the smallest
version."
(or (cl-loop named outer-loop
for entry across qrencode--size-table and version from 1
do (pcase-let ((`(,num-codewords . ,errlevels) entry)
(m (qrencode--length-in-version n version mode)))
(if errcorr
(when (>= (- num-codewords (cadr (assq errcorr errlevels))) m)
(cl-return (cons version errcorr)))
(cl-loop for e in '(H Q M L) ; Go from highest err corr level to lowest
do (when (>= (- num-codewords (cadr (assq e errlevels))) m)
(cl-return-from outer-loop (cons version e)))))))
(user-error "No version found supporting %d in mode %s with error correction level %s" n mode errcorr)))
;;; Structuring
(defun qrencode--get-subseq (blocks n &optional off)
"Return a list of cons of start and end of all subseqs BLOCKS with N bytes.
Optional offset OFF or 0."
(setq off (or off 0))
(cl-loop for i from 0 below blocks
collect (cons (+ (* i n) off)
(+ (* (1+ i) n) off))))
(defun qrencode--get-blocks (version errcorr)
"Return a list of all blocks (subseqs) for VERSION with ERRCORR level."
(pcase-let* ((size-table (aref qrencode--size-table (1- version)))
(num-codewords (car size-table))
(`(,num-errcorr ,_p ,err-blocks) (cdr (assq errcorr (cdr size-table))))
(num-words (- num-codewords num-errcorr)))
(if (consp err-blocks)
(pcase-let ((`(,first-block . ,second-block) err-blocks))
(append
(qrencode--get-subseq (car first-block) (cdr first-block))
(qrencode--get-subseq (car second-block) (cdr second-block) (* (car first-block) (cdr first-block)))))
(qrencode--get-subseq err-blocks (/ num-words err-blocks)))))
(defun qrencode--blocks (data version errcorr)
"Return DATA split in blocks according to VERSION and ERRCORR level."
(cl-loop for b in (qrencode--get-blocks version errcorr)
vconcat (vector (seq-subseq data (car b) (cdr b)))))
;;; QRCode encoding
(defun qrencode (s &optional mode errcorr return-raw)
"Encode string S into a QRCode.
Optionally specify MODE and ERRCORR level. Only supported MODE
is `byte'. If RETURN-RAW is set a raw vector version of the
QRCode is returned instead of a formatted string."
;; Following Section 7.1 from ISO/IEC 18004 2015
(let (version raw-bytes data qr function-pattern datamask)
;; Step 1: Analyse data
;; TODO(#11): find suitable mode. For now we only support byte
(setq mode (or mode 'byte))
;; Convert Emacs internal encoded characters into raw UTF-8 bytes
(setq raw-bytes (encode-coding-string s 'utf-8))
;; Find the version with the highest error correction to fit the data
(pcase-let ((`(,ver . ,ec) (qrencode--find-version (length raw-bytes) mode errcorr)))
(setq version ver
errcorr ec))
;; Step 2: Encode data
(setq data (qrencode--encode-byte raw-bytes version))
;; Add padding
(let* ((size-table (aref qrencode--size-table (1- version)))
(qrlen (car size-table))
(errcorrlen (cadr (assq errcorr (cdr size-table))))
(datalen (- qrlen errcorrlen))
(padding [#xEC #x11]))
(setq data (vconcat data [#x40] ; TODO: why the #x40?
(cl-loop for i from 0 below (- datalen (length data) 1)
vconcat (vector (aref padding (% i 2))))))
;; Step 3: Error correction coding
(let* ((datablocks (qrencode--blocks data version errcorr))
(blockerrcorrlen (/ errcorrlen (length datablocks)))
(field (qrencode--init-field #x11d 2))
(lgen (cadr (qrencode--gen field blockerrcorrlen)))
(errblocks (cl-loop for b across datablocks
vconcat (vector (qrencode--ecc b blockerrcorrlen field lgen)))))
;; Step 4: Structure final message
;; Take from datablocks first until all data is taken and then add the error correction blocks.
(setq data (vconcat (cl-loop for i from 0 below datalen
vconcat (cl-loop for j from 0 below (length datablocks)
when (< i (length (aref datablocks j)))
vconcat (vector (seq-elt (aref datablocks j) i))))
(cl-loop for i from 0 below errcorrlen
vconcat (cl-loop for j from 0 below (length errblocks)
when (< i (length (aref errblocks j)))
vconcat (vector (seq-elt (aref errblocks j) i))))))))
;; Step 5: Module placement
(pcase-let ((`(,qrcode . ,fp) (qrencode--template version)))
(qrencode--draw-data qrcode fp version data)
(setq qr qrcode
function-pattern fp))
;; Step 6: Data masking
(pcase-let ((`(,qrcodemasked . ,mask) (qrencode--find-best-mask qr function-pattern)))
(setq qr qrcodemasked
datamask mask))
;; Step 7: Format and version information
(qrencode--encode-info qr errcorr datamask)
(qrencode--encode-version qr version)
(if return-raw
qr
(qrencode-format qr))))
(defun qrencode-format (qr)
"Format QR using utf-8 blocks."
(let* ((size (length qr))
(sizeqz (+ size 8))) ; size with quiet zone
(concat (make-string sizeqz ? ) "\n"
(make-string sizeqz ? ) "\n"
(cl-loop for row from 0 below (1- size) by 2
concat " "
concat (cl-loop for col from 0 below size
concat (if (and (/= (qrencode--aaref qr col row) 0)
(/= (qrencode--aaref qr col (1+ row)) 0))
"█"
(if (and (/= (qrencode--aaref qr col row) 0)
(= (qrencode--aaref qr col (1+ row)) 0))
"▀"
(if (and (= (qrencode--aaref qr col row) 0)
(/= (qrencode--aaref qr col (1+ row)) 0))
"▄"
" "))))
concat " \n")
" "
(cl-loop for col from 0 below size
concat (if (/= (qrencode--aaref qr col (1- size)) 0) "▀" " "))
" \n"
(make-string sizeqz ? ) "\n"
(make-string sizeqz ? ) "\n")))
(defun qrencode--repeat-string (s n &optional sep)
"Return string S repeated N timed with optional separator SEP in between."
(cl-loop for i from 1 to n
concat s
when (/= i n)
concat (or sep "")))
(defun qrencode-format-as-netpbm (qr &optional pixel-size)
"Format QR as NetPBM (bitmap) file.
Optionally specify PIXEL-SIZE (default is 3)."
(let* ((size (length qr))
(factor (or pixel-size 3))
(nsize (* (+ size 8) factor)))
(concat (format "P1\n%d %d\n" nsize nsize)
;; Quiet zone top
(cl-loop for i from 0 below (* 4 factor)
concat (qrencode--repeat-string "0" nsize " ")
concat "\n")
(cl-loop for row from 0 below size
concat (cl-loop for i from 1 to factor
;; Quiet zone left
concat (qrencode--repeat-string "0 " (* 4 factor))
;; QR Code
concat (cl-loop for col from 0 below size
concat (qrencode--repeat-string
(format "%d " (qrencode--aaref qr col row))
factor))
;; Quiet zone right
concat (qrencode--repeat-string "0" (* 4 factor) " ")
concat "\n"))
;; Quiet zone bottom
(cl-loop for i from 0 below (* 4 factor)
concat (qrencode--repeat-string "0" nsize " ")
concat "\n"))))
(defgroup qrencode nil
"QREncode: Encoder for QR Codes."
:link '(url-link "https://github.com/ruediger/qrencode-el")
:prefix "qrencode-"
:group 'communication)