qrcode 0.0.1 → 0.1.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.
@@ -0,0 +1,269 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2008-2025, by Duncan Robertson.
5
+ # Copyright, 2009, by Chris Mowforth.
6
+ # Copyright, 2011, by Gioele Barabucci.
7
+ # Copyright, 2012-2015, by Björn Blomqvist.
8
+ # Copyright, 2012, by xn.
9
+ # Copyright, 2014, by Sean Doig.
10
+ # Copyright, 2020, by Nathaniel Roman.
11
+ # Copyright, 2021, by Sam Sayer.
12
+ # Copyright, 2025, by Samuel Williams.
13
+
14
+ require_relative "constants"
15
+ require_relative "math"
16
+ require_relative "polynomial"
17
+
18
+ module QRCode
19
+ module Encoder
20
+ class Util
21
+ PATTERN_POSITION_TABLE = [
22
+ [],
23
+ [6, 18],
24
+ [6, 22],
25
+ [6, 26],
26
+ [6, 30],
27
+ [6, 34],
28
+ [6, 22, 38],
29
+ [6, 24, 42],
30
+ [6, 26, 46],
31
+ [6, 28, 50],
32
+ [6, 30, 54],
33
+ [6, 32, 58],
34
+ [6, 34, 62],
35
+ [6, 26, 46, 66],
36
+ [6, 26, 48, 70],
37
+ [6, 26, 50, 74],
38
+ [6, 30, 54, 78],
39
+ [6, 30, 56, 82],
40
+ [6, 30, 58, 86],
41
+ [6, 34, 62, 90],
42
+ [6, 28, 50, 72, 94],
43
+ [6, 26, 50, 74, 98],
44
+ [6, 30, 54, 78, 102],
45
+ [6, 28, 54, 80, 106],
46
+ [6, 32, 58, 84, 110],
47
+ [6, 30, 58, 86, 114],
48
+ [6, 34, 62, 90, 118],
49
+ [6, 26, 50, 74, 98, 122],
50
+ [6, 30, 54, 78, 102, 126],
51
+ [6, 26, 52, 78, 104, 130],
52
+ [6, 30, 56, 82, 108, 134],
53
+ [6, 34, 60, 86, 112, 138],
54
+ [6, 30, 58, 86, 114, 142],
55
+ [6, 34, 62, 90, 118, 146],
56
+ [6, 30, 54, 78, 102, 126, 150],
57
+ [6, 24, 50, 76, 102, 128, 154],
58
+ [6, 28, 54, 80, 106, 132, 158],
59
+ [6, 32, 58, 84, 110, 136, 162],
60
+ [6, 26, 54, 82, 110, 138, 166],
61
+ [6, 30, 58, 86, 114, 142, 170]
62
+ ].freeze
63
+
64
+ G15 = 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0
65
+ G18 = 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0
66
+ G15_MASK = 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1
67
+
68
+ DEMERIT_POINTS_1 = 3
69
+ DEMERIT_POINTS_2 = 3
70
+ DEMERIT_POINTS_3 = 40
71
+ DEMERIT_POINTS_4 = 10
72
+
73
+ BITS_FOR_MODE = {
74
+ MODE[:mode_number] => [10, 12, 14],
75
+ MODE[:mode_alpha_numk] => [9, 11, 13],
76
+ MODE[:mode_8bit_byte] => [8, 16, 16]
77
+ }.freeze
78
+
79
+ # This value is used during the right shift zero fill step. It is
80
+ # auto set to 32 or 64 depending on the arch of your system running.
81
+ # 64 consumes a LOT more memory. In tests it's shown changing it to 32
82
+ # on 64 bit systems greatly reduces the memory footprint. You can use
83
+ # RQRCODE_CORE_ARCH_BITS to make this change but beware it may also
84
+ # have unintended consequences so use at your own risk.
85
+ ARCH_BITS = ENV.fetch("RQRCODE_CORE_ARCH_BITS", nil)&.to_i || 1.size * 8
86
+
87
+ def self.max_size
88
+ PATTERN_POSITION_TABLE.count
89
+ end
90
+
91
+ def self.get_bch_format_info(data)
92
+ d = data << 10
93
+ while Encoder::Util.get_bch_digit(d) - Encoder::Util.get_bch_digit(G15) >= 0
94
+ d ^= (G15 << (Encoder::Util.get_bch_digit(d) - Encoder::Util.get_bch_digit(G15)))
95
+ end
96
+ ((data << 10) | d) ^ G15_MASK
97
+ end
98
+
99
+ def self.rszf(num, count)
100
+ # right shift zero fill
101
+ (num >> count) & ((1 << (ARCH_BITS - count)) - 1)
102
+ end
103
+
104
+ def self.get_bch_version(data)
105
+ d = data << 12
106
+ while Encoder::Util.get_bch_digit(d) - Encoder::Util.get_bch_digit(G18) >= 0
107
+ d ^= (G18 << (Encoder::Util.get_bch_digit(d) - Encoder::Util.get_bch_digit(G18)))
108
+ end
109
+ (data << 12) | d
110
+ end
111
+
112
+ def self.get_bch_digit(data)
113
+ digit = 0
114
+
115
+ while data != 0
116
+ digit += 1
117
+ data = Encoder::Util.rszf(data, 1)
118
+ end
119
+
120
+ digit
121
+ end
122
+
123
+ def self.get_pattern_positions(version)
124
+ PATTERN_POSITION_TABLE[version - 1]
125
+ end
126
+
127
+ def self.get_mask(mask_pattern, i, j)
128
+ if mask_pattern > MASK_COMPUTATIONS.size
129
+ raise RuntimeError, "bad mask_pattern: #{mask_pattern}"
130
+ end
131
+
132
+ MASK_COMPUTATIONS[mask_pattern].call(i, j)
133
+ end
134
+
135
+ def self.get_error_correct_polynomial(error_correct_length)
136
+ a = Encoder::Polynomial.new([1], 0)
137
+
138
+ (0...error_correct_length).each do |i|
139
+ a = a.multiply(Encoder::Polynomial.new([1, Encoder::Math.gexp(i)], 0))
140
+ end
141
+
142
+ a
143
+ end
144
+
145
+ def self.get_length_in_bits(mode, version)
146
+ if !MODE.value?(mode)
147
+ raise RuntimeError, "Unknown mode: #{mode}"
148
+ end
149
+
150
+ if version > 40
151
+ raise RuntimeError, "Unknown version: #{version}"
152
+ end
153
+
154
+ if version.between?(1, 9)
155
+ # 1 - 9
156
+ macro_version = 0
157
+ elsif version <= 26
158
+ # 10 - 26
159
+ macro_version = 1
160
+ elsif version <= 40
161
+ # 27 - 40
162
+ macro_version = 2
163
+ end
164
+
165
+ BITS_FOR_MODE[mode][macro_version]
166
+ end
167
+
168
+ def self.get_lost_points(modules)
169
+ demerit_points = 0
170
+
171
+ demerit_points += Encoder::Util.demerit_points_1_same_color(modules)
172
+ demerit_points += Encoder::Util.demerit_points_2_full_blocks(modules)
173
+ demerit_points += Encoder::Util.demerit_points_3_dangerous_patterns(modules)
174
+ demerit_points += Encoder::Util.demerit_points_4_dark_ratio(modules)
175
+
176
+ demerit_points
177
+ end
178
+
179
+ def self.demerit_points_1_same_color(modules)
180
+ demerit_points = 0
181
+ module_count = modules.size
182
+
183
+ # level1
184
+ (0...module_count).each do |row|
185
+ (0...module_count).each do |col|
186
+ same_count = 0
187
+ dark = modules[row][col]
188
+
189
+ (-1..1).each do |r|
190
+ next if row + r < 0 || module_count <= row + r
191
+
192
+ (-1..1).each do |c|
193
+ next if col + c < 0 || module_count <= col + c
194
+ next if r == 0 && c == 0
195
+ if dark == modules[row + r][col + c]
196
+ same_count += 1
197
+ end
198
+ end
199
+ end
200
+
201
+ if same_count > 5
202
+ demerit_points += (DEMERIT_POINTS_1 + same_count - 5)
203
+ end
204
+ end
205
+ end
206
+
207
+ demerit_points
208
+ end
209
+
210
+ def self.demerit_points_2_full_blocks(modules)
211
+ demerit_points = 0
212
+ module_count = modules.size
213
+
214
+ # level 2
215
+ (0...(module_count - 1)).each do |row|
216
+ (0...(module_count - 1)).each do |col|
217
+ count = 0
218
+ count += 1 if modules[row][col]
219
+ count += 1 if modules[row + 1][col]
220
+ count += 1 if modules[row][col + 1]
221
+ count += 1 if modules[row + 1][col + 1]
222
+ if count == 0 || count == 4
223
+ demerit_points += DEMERIT_POINTS_2
224
+ end
225
+ end
226
+ end
227
+
228
+ demerit_points
229
+ end
230
+
231
+ def self.demerit_points_3_dangerous_patterns(modules)
232
+ demerit_points = 0
233
+ module_count = modules.size
234
+
235
+ # level 3
236
+ modules.each do |row|
237
+ (module_count - 6).times do |col_idx|
238
+ if row[col_idx] && !row[col_idx + 1] && row[col_idx + 2] && row[col_idx + 3] && row[col_idx + 4] && !row[col_idx + 5] && row[col_idx + 6]
239
+ demerit_points += DEMERIT_POINTS_3
240
+ end
241
+ end
242
+ end
243
+
244
+ (0...module_count).each do |col|
245
+ (0...(module_count - 6)).each do |row|
246
+ if modules[row][col] && !modules[row + 1][col] && modules[row + 2][col] && modules[row + 3][col] && modules[row + 4][col] && !modules[row + 5][col] && modules[row + 6][col]
247
+ demerit_points += DEMERIT_POINTS_3
248
+ end
249
+ end
250
+ end
251
+
252
+ demerit_points
253
+ end
254
+
255
+ def self.demerit_points_4_dark_ratio(modules)
256
+ # level 4
257
+ dark_count = modules.reduce(0) do |sum, col|
258
+ sum + col.count(true)
259
+ end
260
+
261
+ # Convert to float to prevent integer division
262
+ ratio = dark_count.to_f / (modules.size * modules.size)
263
+ ratio_delta = (100 * ratio - 50).abs / 5
264
+
265
+ ratio_delta * DEMERIT_POINTS_4
266
+ end
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require_relative "encoder/code"
7
+
8
+ # @namespace
9
+ module QRCode
10
+ # @namespace
11
+ module Encoder
12
+ end
13
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ module QRCode
7
+ module Output
8
+ # SVG renderer for scalable vector graphics
9
+ class SVG
10
+ attr_reader :qrcode, :cell_size, :border, :dark_color, :light_color
11
+
12
+ def initialize(qrcode, cell_size: 10, border: 2, dark_color: "#000000", light_color: "#ffffff")
13
+ @qrcode = qrcode
14
+ @cell_size = cell_size
15
+ @border = border
16
+ @dark_color = dark_color
17
+ @light_color = light_color
18
+ end
19
+
20
+ def render
21
+ total_size = (qrcode.size + (border * 2)) * cell_size
22
+
23
+ svg = []
24
+ svg << %[<?xml version="1.0" encoding="UTF-8"?>]
25
+ svg << %[<svg xmlns="http://www.w3.org/2000/svg" width="#{total_size}" height="#{total_size}" viewBox="0 0 #{total_size} #{total_size}">]
26
+
27
+ # Background rectangle
28
+ svg << %[ <rect x="0" y="0" width="#{total_size}" height="#{total_size}" fill="#{light_color}"/>]
29
+
30
+ # Generate dark modules as rectangles
31
+ qrcode.size.times do |row|
32
+ qrcode.size.times do |col|
33
+ if qrcode.checked?(row, col)
34
+ x = (col + border) * cell_size
35
+ y = (row + border) * cell_size
36
+ svg << %[ <rect x="#{x}" y="#{y}" width="#{cell_size}" height="#{cell_size}" fill="#{dark_color}"/>]
37
+ end
38
+ end
39
+ end
40
+
41
+ svg << %[</svg>]
42
+ svg.join("\n")
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ module QRCode
7
+ module Output
8
+ # Text renderer using Unicode half-height block characters
9
+ class Text
10
+ # Unicode block characters for different combinations
11
+ BLOCKS = {
12
+ [false, false] => " ", # Both light - single space
13
+ [false, true] => "▄", # Top light, bottom dark
14
+ [true, false] => "▀", # Top dark, bottom light
15
+ [true, true] => "█" # Both dark - full block
16
+ }.freeze
17
+
18
+ attr_reader :qrcode, :border
19
+
20
+ def initialize(qrcode, border: 2)
21
+ @qrcode = qrcode
22
+ @border = border
23
+ end
24
+
25
+ def render
26
+ lines = []
27
+
28
+ # Add top border
29
+ border.times do
30
+ lines << " " * total_width
31
+ end
32
+
33
+ # Process QR code in pairs of rows to use half-height blocks
34
+ (0...qrcode.size).step(2) do |row|
35
+ line = " " * border # Left border
36
+
37
+ qrcode.size.times do |col|
38
+ top = qrcode.checked?(row, col)
39
+ bottom = (row + 1 < qrcode.size) ? qrcode.checked?(row + 1, col) : false
40
+ line += BLOCKS[[top, bottom]]
41
+ end
42
+
43
+ line += " " * border # Right border
44
+ lines << line
45
+ end
46
+
47
+ # Add bottom border
48
+ border.times do
49
+ lines << " " * total_width
50
+ end
51
+
52
+ lines.join("\n")
53
+ end
54
+
55
+ private
56
+
57
+ def total_width
58
+ qrcode.size + (border * 2)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,14 @@
1
- module Qrcode
2
- VERSION = "0.0.1"
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2011-2025, by Duncan Robertson.
5
+ # Copyright, 2012-2016, by Björn Blomqvist.
6
+ # Copyright, 2012, by xn.
7
+ # Copyright, 2013, by Yauhen Kharuzhy.
8
+ # Copyright, 2015, by Tonči Damjanić.
9
+ # Copyright, 2015, by Bjorn Blomqvist.
10
+ # Copyright, 2025, by Samuel Williams.
11
+
12
+ module QRCode
13
+ VERSION = "0.1.0"
3
14
  end
data/lib/qrcode.rb CHANGED
@@ -1,5 +1,25 @@
1
- require "qrcode/version"
1
+ # frozen_string_literal: true
2
2
 
3
- module Qrcode
4
- # Your code goes here...
3
+ # Released under the MIT License.
4
+ # Copyright, 2019-2020, by Duncan Robertson.
5
+ # Copyright, 2020, by Nathaniel Roman.
6
+ # Copyright, 2025, by Samuel Williams.
7
+
8
+ require_relative "qrcode/version"
9
+ require_relative "qrcode/encoder"
10
+ require_relative "qrcode/output/text"
11
+ require_relative "qrcode/output/svg"
12
+
13
+ module QRCode
14
+ # Convenience method to create a QR code and render as text
15
+ def self.text(data, level: :h, mode: :auto, size: nil, max_size: nil, **output_options)
16
+ qr = Encoder::Code.build(data, level: level, mode: mode, size: size, max_size: max_size)
17
+ Output::Text.new(qr, **output_options).render
18
+ end
19
+
20
+ # Convenience method to create a QR code and render as SVG
21
+ def self.svg(data, level: :h, mode: :auto, size: nil, max_size: nil, **output_options)
22
+ qr = Encoder::Code.build(data, level: level, mode: mode, size: size, max_size: max_size)
23
+ Output::SVG.new(qr, **output_options).render
24
+ end
5
25
  end
data/license.md ADDED
@@ -0,0 +1,54 @@
1
+ # MIT License
2
+
3
+ Copyright, 2008-2025, by Duncan Robertson.
4
+ Copyright, 2009, by Chris Mowforth.
5
+ Copyright, 2009, by Tore Darell.
6
+ Copyright, 2011, by Daniel Schierbeck.
7
+ Copyright, 2011, by Ken Collins.
8
+ Copyright, 2011, by Gioele Barabucci.
9
+ Copyright, 2011, by Marcos Piccinini.
10
+ Copyright, 2012-2018, by Björn Blomqvist.
11
+ Copyright, 2012, by xn.
12
+ Copyright, 2013, by Yauhen Kharuzhy.
13
+ Copyright, 2013, by Andreas Finger.
14
+ Copyright, 2013-2016, by Bjorn Blomqvist.
15
+ Copyright, 2013, by Jon Evans.
16
+ Copyright, 2014, by Sean Doig.
17
+ Copyright, 2015, by Bart Jedrocha.
18
+ Copyright, 2015, by Simon Males.
19
+ Copyright, 2015, by Ferdinand Rosario.
20
+ Copyright, 2015, by Christopher Lord.
21
+ Copyright, 2015, by José Luis Honorato.
22
+ Copyright, 2015, by Tonči Damjanić.
23
+ Copyright, 2015-2016, by Fabio Napoleoni.
24
+ Copyright, 2016, by Christian Campbell.
25
+ Copyright, 2016, by Andy Brody.
26
+ Copyright, 2017, by Nicolò Gnudi.
27
+ Copyright, 2019, by Thibaut Barrère.
28
+ Copyright, 2019, by Masataka Pocke Kuwabara.
29
+ Copyright, 2020, by Nathaniel Roman.
30
+ Copyright, 2020, by dependabot[bot].
31
+ Copyright, 2020, by Jeremy Evans.
32
+ Copyright, 2021, by Simon Schrape.
33
+ Copyright, 2021, by Sam Sayer.
34
+ Copyright, 2022, by James Neal.
35
+ Copyright, 2025, by Matt Rohrer.
36
+ Copyright, 2025, by Samuel Williams.
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining a copy
39
+ of this software and associated documentation files (the "Software"), to deal
40
+ in the Software without restriction, including without limitation the rights
41
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42
+ copies of the Software, and to permit persons to whom the Software is
43
+ furnished to do so, subject to the following conditions:
44
+
45
+ The above copyright notice and this permission notice shall be included in all
46
+ copies or substantial portions of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
54
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,60 @@
1
+ # QRCode
2
+
3
+ A pure Ruby library for generating QR codes with multiple output formats. Generate QR codes as text art for terminal display or as scalable SVG graphics for web and print applications.
4
+
5
+ This is a fork of [`rqrcode_core`](https://github.com/whomwah/rqrcode_core), which was originally
6
+ adapted in 2008 from a Javascript library by \[Kazuhiko Arase\](https://github.com/kazuhikoarase/
7
+ qrcode-generator).
8
+
9
+ [![Development Status](https://github.com/socketry/qrcode/workflows/Test/badge.svg)](https://github.com/socketry/qrcode/actions?workflow=Test)
10
+
11
+ ## Features
12
+
13
+ - **Pure Ruby**: No external dependencies, works with any Ruby application.
14
+ - **Multiple Output Formats**: Text (Unicode blocks) and SVG output built-in.
15
+ - **Automatic Optimization**: Intelligently selects the most efficient encoding (numeric, alphanumeric, or binary).
16
+ - **Error Correction**: Full support for all four standardized error correction levels (L, M, Q, H).
17
+ - **Multi-Segment Encoding**: Optimize large data by mixing encoding modes in a single QR code.
18
+ - **Command Line Tools**: Bake tasks for generating QR codes from the terminal.
19
+ - **Standards Compliant**: Follows ISO/IEC 18004 QR Code specification.
20
+
21
+ ## Usage
22
+
23
+ Please see the [project documentation](https://socketry.github.io/qrcode/) for more details.
24
+
25
+ - [Getting Started](https://socketry.github.io/qrcode/guides/getting-started/index) - This guide explains how to get started with `qrcode` to generate QR codes in Ruby.
26
+
27
+ ## Releases
28
+
29
+ Please see the [project releases](https://socketry.github.io/qrcode/releases/index) for all releases.
30
+
31
+ ### v0.1.0
32
+
33
+ - **Breaking**: Complete refactor of encoder architecture with cleaner segment-based design.
34
+ - **Breaking**: Renamed `RSBlock` to `ErrorCorrectionBlock` with cleaner method names (`for`, `table_entry_for`).
35
+ - **Breaking**: Simplified `Code` constructor to take segments array, added `Code.build()` factory method.
36
+ - **Breaking**: Removed redundant `Multi` class - multi-segment support now built into `Code` directly.
37
+ - Added self-contained segment classes: `Segment`, `NumericSegment`, `AlphanumericSegment`.
38
+ - Added comprehensive test coverage for output functionality (Text and SVG).
39
+ - Added `size` alias for `module_count` for cleaner API.
40
+ - Added proper documentation explaining error correction level encoding from ISO/IEC 18004.
41
+ - Improved code organization with `QRCode::Encoder` namespace for all encoding classes.
42
+ - Removed QR prefix from encoder file and class names for cleaner codebase.
43
+
44
+ ## Contributing
45
+
46
+ We welcome contributions to this project.
47
+
48
+ 1. Fork it.
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
51
+ 4. Push to the branch (`git push origin my-new-feature`).
52
+ 5. Create new Pull Request.
53
+
54
+ ### Developer Certificate of Origin
55
+
56
+ In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
57
+
58
+ ### Community Guidelines
59
+
60
+ This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
data/releases.md ADDED
@@ -0,0 +1,14 @@
1
+ # Releases
2
+
3
+ ## v0.1.0
4
+
5
+ - **Breaking**: Complete refactor of encoder architecture with cleaner segment-based design.
6
+ - **Breaking**: Renamed `RSBlock` to `ErrorCorrectionBlock` with cleaner method names (`for`, `table_entry_for`).
7
+ - **Breaking**: Simplified `Code` constructor to take segments array, added `Code.build()` factory method.
8
+ - **Breaking**: Removed redundant `Multi` class - multi-segment support now built into `Code` directly.
9
+ - Added self-contained segment classes: `Segment`, `NumericSegment`, `AlphanumericSegment`.
10
+ - Added comprehensive test coverage for output functionality (Text and SVG).
11
+ - Added `size` alias for `module_count` for cleaner API.
12
+ - Added proper documentation explaining error correction level encoding from ISO/IEC 18004.
13
+ - Improved code organization with `QRCode::Encoder` namespace for all encoding classes.
14
+ - Removed QR prefix from encoder file and class names for cleaner codebase.