qrest 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/qrest/base.rb +13 -0
- data/lib/qrest/bch.rb +57 -0
- data/lib/qrest/bitbuffer.rb +85 -0
- data/lib/qrest/bitstream.rb +27 -0
- data/lib/qrest/demerits.rb +118 -0
- data/lib/qrest/foreign/supplement.rb +51 -0
- data/lib/qrest/formats/eps.rb +65 -0
- data/lib/qrest/formats/svg.rb +77 -0
- data/lib/qrest/formats/xpm.rb +48 -0
- data/lib/qrest/modules.rb +300 -0
- data/lib/qrest/polynomial.rb +136 -0
- data/lib/qrest/rsblocks.rb +143 -0
- data/lib/qrest/segment.rb +210 -0
- data/lib/qrest/version.rb +12 -0
- data/lib/qrest.rb +67 -0
- metadata +56 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 28f37a9b5e5fb20a07f781323ce9f2381ecadd873bec290b5b65811849adb563
|
4
|
+
data.tar.gz: 7ba9b8e971a1e76bb5869455a15f0973f29575a82a11d671656070a16ae1cfe0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5d592804716c69561ceb2cd556c7581302a485c7925c6908d956ebeeb7277011da21cf07867af837942813073d7e0307b7af27e74f7911180b8bbf1a8c11ff23
|
7
|
+
data.tar.gz: 72830cd5baadaefc25cade7df5a7a82af010a79ecb78f54e576c90551b09a2e072f943c42306f31e8fd873befaba0d63fdb495b26cb4188f9cbca3234b8c0e30
|
data/lib/qrest/base.rb
ADDED
data/lib/qrest/bch.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#
|
2
|
+
# qrest/bch.rb -- Bose–Chaudhuri–Hocquenghem codes
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/base"
|
6
|
+
|
7
|
+
|
8
|
+
module QRest
|
9
|
+
|
10
|
+
module Bch
|
11
|
+
|
12
|
+
class <<self
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def digit data
|
17
|
+
n = 0
|
18
|
+
while data != 0 do
|
19
|
+
data >>= 1
|
20
|
+
n += 1
|
21
|
+
end
|
22
|
+
n
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
G15 = 0b000010100110111
|
28
|
+
G15_MASK = 0b101010000010010
|
29
|
+
G18 = 0b001111100100101
|
30
|
+
|
31
|
+
G15D = digit G15
|
32
|
+
G18D = digit G18
|
33
|
+
|
34
|
+
class <<self
|
35
|
+
|
36
|
+
def format_info data
|
37
|
+
d = data << 10
|
38
|
+
until (m = (digit d) - G15D) < 0 do
|
39
|
+
d ^= G15 << m
|
40
|
+
end
|
41
|
+
((data << 10) | d) ^ G15_MASK
|
42
|
+
end
|
43
|
+
|
44
|
+
def version data
|
45
|
+
d = data << 12
|
46
|
+
until (m = (digit d) - G18D) < 0 do
|
47
|
+
d ^= G18 << m
|
48
|
+
end
|
49
|
+
(data << 12) | d
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
#
|
2
|
+
# qrest/bitbuffer.rb -- Bit buffer
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/base"
|
6
|
+
|
7
|
+
|
8
|
+
module QRest
|
9
|
+
|
10
|
+
class BitBuffer
|
11
|
+
|
12
|
+
class <<self
|
13
|
+
def build max
|
14
|
+
i = new
|
15
|
+
yield i
|
16
|
+
i.finish max
|
17
|
+
i.cont
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :cont
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@cont, @rem = [], 0
|
25
|
+
end
|
26
|
+
|
27
|
+
# You want to see an example for premature optimization
|
28
|
+
# turning into weird code? Okay, here we go.
|
29
|
+
|
30
|
+
def put num, length
|
31
|
+
n = []
|
32
|
+
l = length - @rem
|
33
|
+
if l > 0 then
|
34
|
+
if (r = l % 8).zero? then
|
35
|
+
prem = 0
|
36
|
+
else
|
37
|
+
prem = 8 - r
|
38
|
+
n.unshift num << prem
|
39
|
+
num >>= r
|
40
|
+
length -= r
|
41
|
+
end
|
42
|
+
while length >= 8 do
|
43
|
+
n.unshift num
|
44
|
+
num >>= 8
|
45
|
+
length -= 8
|
46
|
+
end
|
47
|
+
else
|
48
|
+
prem = -l
|
49
|
+
num <<= prem
|
50
|
+
end
|
51
|
+
@cont.last |= num if length > 0
|
52
|
+
@cont.concat n.map { |b| b & 0xff }
|
53
|
+
@rem = prem
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def put_bit bit
|
58
|
+
if @rem.zero? then
|
59
|
+
@rem = 7
|
60
|
+
@mask = 1 << @rem
|
61
|
+
@cont.push 0
|
62
|
+
else
|
63
|
+
@mask >>= 1
|
64
|
+
@rem -= 1
|
65
|
+
end
|
66
|
+
@cont.last |= @mask if bit
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
PAD = [ 0xec, 0x11, ]
|
71
|
+
|
72
|
+
def finish max
|
73
|
+
@cont.length <= max or raise Error, "Code length overflow: #{length}>#{max}"
|
74
|
+
@cont.push 0 if @rem < 4 and @cont.length < max
|
75
|
+
@rem = nil
|
76
|
+
pad = max - @cont.length
|
77
|
+
ps = PAD * (pad/2+1)
|
78
|
+
ps.pop if pad.odd?
|
79
|
+
@cont.concat ps
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# qrest/bitstream.rb -- Bit Stream
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/base"
|
6
|
+
|
7
|
+
|
8
|
+
module QRest
|
9
|
+
|
10
|
+
class BitStream
|
11
|
+
|
12
|
+
def initialize data
|
13
|
+
@cur, @data = [], data.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
def get
|
17
|
+
if @cur.empty? and not @data.empty? then
|
18
|
+
n = @data.shift
|
19
|
+
8.times { @cur.push n.odd? ; n >>= 1 }
|
20
|
+
end
|
21
|
+
@cur.pop
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
#
|
2
|
+
# qrest/demerits.rb -- Compute demerits
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/base"
|
6
|
+
|
7
|
+
|
8
|
+
module QRest
|
9
|
+
|
10
|
+
class Demerits
|
11
|
+
|
12
|
+
def initialize fields
|
13
|
+
@fields = fields
|
14
|
+
end
|
15
|
+
|
16
|
+
def total **weigths
|
17
|
+
weigths.inject 0 do |sum,(k,v)|
|
18
|
+
e = ([*(send k)].zip [*v]).inject 0 do |s,(r,f)| s += r*f end
|
19
|
+
sum += e.round
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def same_color
|
26
|
+
points, add = 0, 0
|
27
|
+
each_pixel do |row,col|
|
28
|
+
same = 0
|
29
|
+
dark = @fields[row][col]
|
30
|
+
each_around row, col do |d|
|
31
|
+
same += 1 if dark == d
|
32
|
+
end
|
33
|
+
a = same - 5
|
34
|
+
if a > 0 then
|
35
|
+
points += 1
|
36
|
+
add += a
|
37
|
+
end
|
38
|
+
end
|
39
|
+
[ points, add]
|
40
|
+
end
|
41
|
+
|
42
|
+
def each_pixel
|
43
|
+
@fields.size.times do |row|
|
44
|
+
@fields.size.times do |col|
|
45
|
+
yield row, col
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def same_region i
|
51
|
+
n = i+1
|
52
|
+
(i == 0 ? 0 : -1)..(n >= @fields.size ? 0 : 1)
|
53
|
+
end
|
54
|
+
|
55
|
+
def each_around row, col
|
56
|
+
(same_region row).each do |r|
|
57
|
+
rn = row + r
|
58
|
+
(same_region col).each do |c|
|
59
|
+
next if r == 0 && c == 0
|
60
|
+
cn = col + c
|
61
|
+
yield @fields[rn][cn]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def full_blocks
|
68
|
+
points = 0
|
69
|
+
each_block do |b|
|
70
|
+
b.uniq!
|
71
|
+
points += 1 unless b.length > 1
|
72
|
+
end
|
73
|
+
points
|
74
|
+
end
|
75
|
+
|
76
|
+
def each_block
|
77
|
+
sp = @fields.size - 1
|
78
|
+
sp.times do |ri|
|
79
|
+
rn = ri + 1
|
80
|
+
sp.times do |ci|
|
81
|
+
cn = ci + 1
|
82
|
+
yield [ @fields[ri][ci], @fields[rn][ci], @fields[ri][cn], @fields[rn][cn], ]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def dangerous
|
89
|
+
points = 0
|
90
|
+
@fields.each do |rs|
|
91
|
+
rs.each_cons 7 do |fs|
|
92
|
+
points += 1 if is_probe? fs
|
93
|
+
end
|
94
|
+
end
|
95
|
+
@fields.each_cons 7 do |rs|
|
96
|
+
@fields.size.times do |c|
|
97
|
+
points += 1 if is_probe? rs.map { |r| r[c] }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
points
|
101
|
+
end
|
102
|
+
|
103
|
+
def is_probe? fields
|
104
|
+
f0, f1, f2, f3, f4, f5, f6 = fields
|
105
|
+
f0 && !f1 && f2 && f3 && f4 && !f5 && f6
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def dark_ratio
|
110
|
+
dark = @fields.sum do |col| col.count true end
|
111
|
+
all = @fields.size*@fields.size
|
112
|
+
(dark.to_f / all - 0.5).abs * 100
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#
|
2
|
+
# qrest/foreign/supplement.rb -- Additional useful Ruby functions
|
3
|
+
#
|
4
|
+
|
5
|
+
# The purpose of this is simply to reduce dependencies.
|
6
|
+
|
7
|
+
begin
|
8
|
+
require "supplement"
|
9
|
+
rescue LoadError
|
10
|
+
class NilClass ; def notempty? ; end ; end
|
11
|
+
class String ; def notempty? ; self unless empty? ; end ; end
|
12
|
+
class Array ; def notempty? ; self unless empty? ; end ; end
|
13
|
+
class Array ; def first= val ; self[ 0] = val ; end ; end
|
14
|
+
class Array ; def last= val ; self[-1] = val ;
|
15
|
+
rescue IndexError ; a.push val ; end ; end
|
16
|
+
class NilClass ; def to_bool ; false ; end ; end
|
17
|
+
class FalseClass ; def to_bool ; false ; end ; end
|
18
|
+
class Object ; def to_bool ; true ; end ; end
|
19
|
+
class <<Struct ; alias [] new ; end
|
20
|
+
class Module
|
21
|
+
def plain_name
|
22
|
+
sep = "::"
|
23
|
+
n = name.dup
|
24
|
+
i = n.rindex sep
|
25
|
+
n.slice! 0, i+sep.length if i
|
26
|
+
n
|
27
|
+
end
|
28
|
+
end
|
29
|
+
class String
|
30
|
+
def axe n
|
31
|
+
if n < length then
|
32
|
+
e = "..."
|
33
|
+
l = e.length
|
34
|
+
if n > l then
|
35
|
+
n -= l
|
36
|
+
else
|
37
|
+
l = 0
|
38
|
+
end
|
39
|
+
(slice 0, n) << "..."[0,l]
|
40
|
+
else
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
def starts_with? oth ; o = oth.to_str ; o.length if start_with? o ; end
|
45
|
+
def ends_with? oth ; o = oth.to_str ; length - o.length if end_with? o ; end
|
46
|
+
alias starts_with starts_with?
|
47
|
+
alias ends_with ends_with?
|
48
|
+
end
|
49
|
+
class Dir ; def entries! ; entries - %w(. ..) ; end ; end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#
|
2
|
+
# qrest/formats/xpm.rb -- Build XPM
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/modules"
|
6
|
+
require "qrest/version"
|
7
|
+
|
8
|
+
|
9
|
+
module QRest
|
10
|
+
|
11
|
+
class Modules
|
12
|
+
|
13
|
+
def eps quiet_size: nil, output: nil
|
14
|
+
quiet_size ||= 4
|
15
|
+
output ||= ""
|
16
|
+
|
17
|
+
full = size + 2*quiet_size
|
18
|
+
output << <<~EOT
|
19
|
+
%!PS
|
20
|
+
%%Creator: #{QRest::NAME} #{QRest::VERSION}
|
21
|
+
%%BoundingBox: 0 0 #{full} #{full}
|
22
|
+
%%EndComments
|
23
|
+
/box { newpath moveto 0 -1 rlineto 1 0 rlineto 0 1 rlineto closepath fill } def
|
24
|
+
EOT
|
25
|
+
LinesOut.open output do |lo|
|
26
|
+
each_field_neg quiet_size, full do |ri,ci|
|
27
|
+
lo.put "%d %d box" % [ci,ri]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
output << <<~EOT
|
31
|
+
showpage
|
32
|
+
EOT
|
33
|
+
output
|
34
|
+
end
|
35
|
+
|
36
|
+
class LinesOut
|
37
|
+
class <<self
|
38
|
+
private :new
|
39
|
+
def open output
|
40
|
+
i = new output
|
41
|
+
yield i
|
42
|
+
output << "\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
MAX = 78
|
46
|
+
def initialize output
|
47
|
+
@output = output
|
48
|
+
@line = 0
|
49
|
+
end
|
50
|
+
def put str
|
51
|
+
@line += str.length
|
52
|
+
if @line > MAX then
|
53
|
+
@output << "\n"
|
54
|
+
@line = 0
|
55
|
+
else
|
56
|
+
@output << " "
|
57
|
+
end
|
58
|
+
@output << str
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# qrest/formats/svg.rb -- Build SVG
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/modules"
|
6
|
+
require "qrest/version"
|
7
|
+
|
8
|
+
|
9
|
+
module QRest
|
10
|
+
|
11
|
+
class Modules
|
12
|
+
|
13
|
+
def svg header: nil, dimen: nil, quiet_size: nil, title: nil, cls: nil, id: nil, output: nil
|
14
|
+
header = true if header.nil?
|
15
|
+
dimen ||= "100px"
|
16
|
+
quiet_size ||= 4
|
17
|
+
title ||= "QR Code"
|
18
|
+
cls ||= "qr"
|
19
|
+
id ||= self.class.svg_id
|
20
|
+
output ||= ""
|
21
|
+
|
22
|
+
header and output << <<~EOT
|
23
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
24
|
+
EOT
|
25
|
+
full = size + 2*quiet_size
|
26
|
+
output << <<~EOT
|
27
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
28
|
+
version="1.1" baseProfile="full"
|
29
|
+
class="#{cls}" id="#{id}"
|
30
|
+
width="#{dimen}" height="#{dimen}" viewBox="0 0 #{full} #{full}">
|
31
|
+
<title>#{title}</title>
|
32
|
+
<desc>Generated by #{QRest::NAME} #{QRest::VERSION}</desc>
|
33
|
+
EOT
|
34
|
+
each_field quiet_size do |ri,ci|
|
35
|
+
output << <<~EOT % [ci,ri]
|
36
|
+
<rect x="%3d" y="%3d" width="1" height="1" fill="black"/>
|
37
|
+
EOT
|
38
|
+
end
|
39
|
+
output << <<~EOT
|
40
|
+
</svg>
|
41
|
+
EOT
|
42
|
+
output
|
43
|
+
end
|
44
|
+
|
45
|
+
def html dimen: nil, quiet_size: nil, title: nil, output: nil
|
46
|
+
output ||= ""
|
47
|
+
output << <<~EOT
|
48
|
+
<!DOCTYPE html>
|
49
|
+
<html>
|
50
|
+
<head>
|
51
|
+
<meta charset="UTF-8">
|
52
|
+
<title>Example Page with QR Code</title>
|
53
|
+
<style>
|
54
|
+
body { background: #ddddee; }
|
55
|
+
.qrcode { background: #ffffff; }
|
56
|
+
</style>
|
57
|
+
</head>
|
58
|
+
<body>
|
59
|
+
<h1>My self-generated QR Code</h1>
|
60
|
+
EOT
|
61
|
+
svg header: false, quiet_size: quiet_size, title: title, cls: "qrcode", output: output
|
62
|
+
output << <<~EOT
|
63
|
+
<hr>
|
64
|
+
<pre>#{Time.now}</pre>
|
65
|
+
</body>
|
66
|
+
</html>
|
67
|
+
EOT
|
68
|
+
end
|
69
|
+
|
70
|
+
class <<self
|
71
|
+
def svg_id ; @id ||= "qr_000" ; @id.succ! ; end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#
|
2
|
+
# qrest/formats/xpm.rb -- Build XPM
|
3
|
+
#
|
4
|
+
|
5
|
+
require "qrest/modules"
|
6
|
+
require "qrest/version"
|
7
|
+
|
8
|
+
|
9
|
+
module QRest
|
10
|
+
|
11
|
+
class Modules
|
12
|
+
|
13
|
+
XPM_D, XPM_L = "X", "."
|
14
|
+
|
15
|
+
def xpm name: nil, pixels: nil, quiet_size: nil, output: nil
|
16
|
+
name ||= QRest::NAME
|
17
|
+
name = name[ /[a-z_][a-z_0-9]*/i]
|
18
|
+
pixels ||= 3
|
19
|
+
quiet_size ||= 4
|
20
|
+
output ||= ""
|
21
|
+
|
22
|
+
output << <<~EOT
|
23
|
+
/* XPM */
|
24
|
+
static char * #{name}[] = {
|
25
|
+
EOT
|
26
|
+
dim = (size + 2*quiet_size)*pixels
|
27
|
+
output << <<~EOT
|
28
|
+
"#{dim} #{dim} 2 1 0 0 XPMEXT",
|
29
|
+
EOT
|
30
|
+
output << <<~EOT
|
31
|
+
"#{XPM_L}\tc #ffffff",
|
32
|
+
"#{XPM_D}\tc #000000",
|
33
|
+
EOT
|
34
|
+
lines dark: XPM_D*pixels, light: XPM_L*pixels, quiet_size: quiet_size do |l|
|
35
|
+
pixels.times { output << "\"#{l}\",\n" }
|
36
|
+
end
|
37
|
+
output << <<~EOT
|
38
|
+
"XPMEXT generator #{QRest::NAME} #{QRest::VERSION}",
|
39
|
+
"XPMENDEXT"
|
40
|
+
};
|
41
|
+
EOT
|
42
|
+
output
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|