qr_forge 1.1.0 → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39cc8a8482cee2898925eab861d71d2a96e778d61d34e3e3e6d2774f8ec3fa95
4
- data.tar.gz: d217725f654c5b08ee9e7addb7d04f04a8b7e9495b2809210c5ead98e933deb2
3
+ metadata.gz: 68e6f3102dc3d6954c036f6bfb17a2820743956eea34b7ef11565f173cab0a9a
4
+ data.tar.gz: e3d5a5c12559c0cf707537ce5163d10717e0f1c18a39130cfb5ba940bc71647d
5
5
  SHA512:
6
- metadata.gz: cc85e4a4f63070f81e69d2a7c715b17a9fccb5dd19469a4a72dd37ebfb40245b073b79e2a35357b5b0edfb73ca812f32be6651cf7afb496fc0af419093b87c9e
7
- data.tar.gz: 48a38d7344bdc09748a0bd546a3466f4e403a90611ef85d40c6f9418cbf569f18b7c797d4dae346e8b192d14ed6ae836020bceddc51a855e1dc700b2dfa70540
6
+ metadata.gz: 33ba1ce62ea6a782dcc019c72f4c94f487c0ea49847485ae888fc9729b27a710741a5a6d5a771113f1617bbec52ba8e5a9d007b9dfc83ac574b08a7c6031c42a
7
+ data.tar.gz: 1f245ef3f6a10dadc97c8eb60d8d74e27aeff4f0258c0cf84989e39c85179c4fe027a1c204f0ef2df838c100b9039feb34d3712a373d986816a579735a09f371
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 3.1
3
+ NewCops: enable
3
4
 
4
5
  Style/StringLiterals:
5
6
  EnforcedStyle: double_quotes
@@ -17,4 +18,7 @@ Metrics/ParameterLists:
17
18
  Max: 10
18
19
 
19
20
  Metrics/BlockLength:
20
- Max: 150
21
+ Max: 150
22
+
23
+ Metrics/ClassLength:
24
+ Max: 500
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- # !! Docs are still being written !! #
2
- ![output](https://github.com/user-attachments/assets/71edb1aa-2182-4cd3-bb28-ec4f71336029)
1
+ ![output](https://github.com/user-attachments/assets/6d360bbb-511d-43f4-a356-8adf634efac0)
3
2
 
4
3
  An example of the output of QR Forge
5
4
 
@@ -19,177 +18,160 @@ or if using bundler
19
18
  bundle add qr_forge
20
19
  ```
21
20
 
22
- Note: To export in the PNG format you will also want to install vips on your machine
23
-
24
- ```shell
25
- brew install vips
26
- ```
27
-
28
21
  # Usage
29
22
 
30
23
  To create a QR Code you can simply do the following:
31
24
 
25
+ ### Payloads
26
+
27
+ Did you know that QR Codes can store different pieces of data such as wifi access, passkeys, geolocation and quite a few more! Below are a few different types of data payloads that are implemented with in the gem. More will be added as needed.
28
+
29
+ #### Plain text
30
+
32
31
  ```ruby
33
- QrForge::Forge.build(text: "https://yourlinkhere.com")
32
+ QrForge::Forge.build(data: "https://yourlinkhere.com", type: :plain)
34
33
  ```
35
34
 
36
- If you want an (SVG) QRCode fast, that's all there is to it. Everything else is covered by default values, however, you can tweak anything you want, which we will go through in the next section.
35
+ #### URL
37
36
 
38
- ## Configuration
37
+ ```ruby
38
+ QrForge::Forge.build(data: "https://yourlinkhere.com", type: :url)
39
+ ```
39
40
 
40
- The library tries to provide sensible defaults, but defaults can be subjective, so the following shows a configuration object that you can pass to the QR Code.
41
+ #### Wifi
41
42
 
42
43
  ```ruby
43
- QrForge::Forge.build(
44
- text: "https://www.google.com",
45
- config: {
46
- qr: { version: 10 },
47
- components: { inner_eye: QrForge::Components::EyeInner::Square },
48
- design: {
49
- size: 800,
50
- colors: { module: 'blue', outer_eye: 'cyan', inner_eye: 'skyblue' },
51
- image: Base64.strict_encode64(...)
52
- },
53
- output: { format: :png }
54
- }
55
- )
44
+ QrForge::Payload.build(data: { ssid: "MyNetwork", password: "MyPassword", encryption: "WPA" }, type: :wifi)
56
45
  ```
46
+ Fields:
47
+ - ssid,
48
+ - password
49
+ - encryption (WEP WPA WPA2 WPA3)
50
+ - hidden (true/false)
57
51
 
58
- ##### QR
52
+ >[!NOTE]
53
+ >You can also pass "nopass" to encryption which means a password does not need to be provided either (public wifi etc.)
54
+
55
+ #### Geo/Coordinates
59
56
 
60
- This hash provides details to the underlying QR code generator (see: https://github.com/whomwah/rqrcode_core).
57
+ ```ruby
58
+ QrForge::Payload.build(data: { latitude: 40.712776, longitude: -74.005974 }, type: :geo)
59
+ ```
60
+ Fields:
61
+ - latitude
62
+ - longitude
61
63
 
62
- _version_ dictates the module count or size of the QR Code and how much data it can hold. It does not necessarily dictate the dimensions, however, it will be unreadable if you choose too small of a size, but a larger QR code version. (see: https://www.qrcode.com/en/about/version.html)
64
+ #### Phone number
63
65
 
64
- ##### Components
66
+ ```ruby
67
+ QrForge::Payload.build(data: "+1234567890", type: :phone)
68
+ QrForge::Payload.build(data: "1234567890", type: :phone)
69
+ QrForge::Payload.build(data: "123-456-7890", type: :phone)
70
+ QrForge::Payload.build(data: "(123) 456-7890", type: :phone)
71
+ QrForge::Payload.build(data: "123.456.7890", type: :phone)
72
+ ```
65
73
 
66
- This hash can have up to 3 components:
67
- - outer_eye
68
- <img width="77" alt="Screenshot 2025-06-17 at 1 23 39 AM" src="https://github.com/user-attachments/assets/ccef3f08-cc4b-43c7-95b4-8d6c707f5f5a" />
74
+ More are planned to be added.
69
75
 
70
- - inner_eye
71
- <img width="77" alt="Screenshot 2025-06-17 at 1 24 22 AM" src="https://github.com/user-attachments/assets/c21bb175-00c2-4179-a20a-4e505ad37df1" />
76
+ ### Design
72
77
 
73
- - module
74
- <img width="216" alt="Screenshot 2025-06-17 at 1 25 24 AM" src="https://github.com/user-attachments/assets/f670d5fd-e2dc-42cc-9bff-d77a51d65fa1" />
78
+ Design controls the look and feel of the QR Code from the [QR Version](https://www.qrcode.com/en/about/version.html) to the colors of the individual modules.
75
79
 
76
- This is what the circle outer_eye component looks like:
80
+ ##### QR
77
81
 
78
82
  ```ruby
79
- # frozen_string_literal: true
80
-
81
- module QrForge
82
- module Components
83
- module EyeOuter
84
- class Circle < ForgeComponent
85
- DEFAULT_STROKE_WIDTH = 1.0
86
-
87
- # @see ForgeComponent#draw
88
- # Draws a circle that fills the full 'area' box, inset by half the stroke so it
89
- # never overlaps the modules beneath.
90
- def draw(y:, x:, quiet_zone:, area:, color: "black", **_)
91
- stroke_width = DEFAULT_STROKE_WIDTH
92
-
93
- # Radius = (full width of box – one stroke) / 2
94
- r = (area - stroke_width) / 2.0
95
-
96
- # Center of the N×N box (plus quiet_zone offset)
97
- cx = x + quiet_zone + (area / 2.0)
98
- cy = y + quiet_zone + (area / 2.0)
99
-
100
- @xml_builder.circle(
101
- cx: cx,
102
- cy: cy,
103
- r: r,
104
- 'stroke-width': stroke_width,
105
- stroke: color,
106
- fill: "transparent",
107
- test_id: @test_id
108
- )
109
- end
110
- end
111
- end
112
- end
113
- end
83
+ QrForge::Payload.build(..., design: { qr { version: 10 } }, ...)
114
84
  ```
85
+ Fields:
86
+ - Version
87
+
88
+ ##### Image
115
89
 
116
- And the square for comparison:
90
+ ```ruby
91
+ QrForge::Payload.build(..., design: { image: "..." }, ...)
92
+ ```
93
+ The image should be a **Base64 encoded** image like the following:
117
94
 
118
95
  ```ruby
119
- # frozen_string_literal: true
120
-
121
- module QrForge
122
- module Components
123
- module EyeOuter
124
- class Square < ForgeComponent
125
- # @see ForgeComponent#draw
126
- def draw(y:, x:, quiet_zone:, area:, color: "black", **_)
127
- x += quiet_zone
128
- y += quiet_zone
129
-
130
- # Draw the outer black square (7x7)
131
- @xml_builder.rect(
132
- x:,
133
- y:,
134
- width: area,
135
- height: area,
136
- fill: color
137
- )
138
-
139
- # Draw the inner (cutout) square (5x5) to create the finder pattern
140
- inset = 1
141
- inner = area - 2
142
-
143
- @xml_builder.rect(
144
- x: x + inset,
145
- y: y + inset,
146
- width: inner,
147
- height: inner,
148
- fill: color,
149
- test_id: @test_id
150
- )
151
- end
152
- end
153
- end
154
- end
155
- end
96
+ Base64.strict_encode64(image_png_binary)
156
97
  ```
98
+ More image formats will be added as needed or required.
157
99
 
158
- All components must inherit from the base ForgeComponent. You can look at that to see what is available to be used (this is on the roadmap to allow more design variations).
100
+ ##### Colors
159
101
 
160
- If you create your own designs, follow the same pattern and pass them into the components config. It will merge them with the default components, so if you only want to change one or two of the components, but not all three, you can!
102
+ ```ruby
103
+ QrForge::Payload.build(..., design: {colors: { outer_eye: "#30363D", inner_eye: "#30363D", module: "#484F58" }, ...)
104
+ ```
105
+ Fields:
106
+ - outer_eye
107
+ - inner_eye
108
+ - module
109
+
110
+ Any hex code or plain color such as "red", "cyan", "peru", etc. will work as the color value.
161
111
 
162
- ##### Design
112
+ See the Components section for more details on which field references what component of the QR Code.
163
113
 
114
+ ### Output
115
+
116
+ ```ruby
117
+ QrForge::Payload.build(..., output: { size: 250 }, ...)
164
118
  ```
165
- size: 800
166
- ```
119
+ Fields:
120
+ - size
121
+
122
+ QR Codes are equal in height and width, the size will control the width and height of the returned SVG.
167
123
 
168
- This sets the width and height of the svg. I recommend that you set this to a higher value than you plan to use as the SVG will scale well without needing to use any image processing like vips. If you do want to use your own strategies for image processing, I still recommend a higher size and do with the SVG as you wish.
124
+ There is only an ouput of SVG format, see this PR for reasoning: https://github.com/keegankb93/qr_forge/pull/1
169
125
 
170
- ```
171
- colors: { module: 'blue', outer_eye: 'cyan', inner_eye: 'skyblue' }
172
- ```
126
+ If different formats are a requirement and that requirement means the image conversion should take place in the gem, send me a message with your usecase. SVG as the format should be good for almost all usecases.
173
127
 
174
- This will set the fill or stroke appropriately for the respective component.
128
+ ### Components
175
129
 
176
- ```
177
- image: ...
130
+ ```ruby
131
+ QrForge::Payload.build(..., components: {
132
+ outer_eye: QrForge::Components::EyeOuter::Square,
133
+ inner_eye: QrForge::Components::EyeInner::Square,
134
+ module: QrForge::Components::Module::Squre
135
+ },
136
+ ...)
178
137
  ```
138
+ Fields:
139
+ - outer_eye
140
+ <img width="77" alt="Screenshot 2025-06-17 at 1 23 39 AM" src="https://github.com/user-attachments/assets/ccef3f08-cc4b-43c7-95b4-8d6c707f5f5a" />
179
141
 
180
- A base64 encoded image that will be placed in the center of your QR Code. This scales with the version of the QR Code and is strictly set as to make sure that we do not remove more data that can be recovered through the error correcting algorithms.
142
+ - inner_eye
143
+ <img width="77" alt="Screenshot 2025-06-17 at 1 24 22 AM" src="https://github.com/user-attachments/assets/c21bb175-00c2-4179-a20a-4e505ad37df1" />
181
144
 
182
- ##### Output
145
+ - module
146
+ <img width="216" alt="Screenshot 2025-06-17 at 1 25 24 AM" src="https://github.com/user-attachments/assets/f670d5fd-e2dc-42cc-9bff-d77a51d65fa1" />
183
147
 
184
- ```
185
- output: { format: :png }})
186
- ```
148
+ You can create your own designs and pass them through as long as they inherit from the ForgeComponent. If you create your own designs, when you pass them through the config, it will merge them with the default components, so if you only want to change one or two of the components, but not all three, you can!
149
+
150
+ >[!NOTE]
151
+ >More designs are to come, so not every facet of qr code manipulation may be available at this time such as module look aheads/behinds. This is >coming soon.
152
+
153
+ ### Example
187
154
 
188
- This will use vips to process the svg and return it as a PNG. If you want to do your own image processing, you can leave this off as the default is the raw SVG string.
155
+ This is the code that was used to create the QR Code at the top of the page.
189
156
 
157
+ ```ruby
158
+ QrForge::Forge.build(data: "https://www.github.com/keegankb93/qr_forge",
159
+ type: :url,
160
+ config: {
161
+ qr: { version: 5 },
162
+ design: {
163
+ image:,
164
+ colors: {
165
+ outer_eye: "#30363D",
166
+ inner_eye: "#30363D",
167
+ module: "#484F58"
168
+ }
169
+ },
170
+ output: { size: 250 }
171
+ })
172
+ ```
190
173
 
191
- ## Roadmap
192
- - Color gradients for components
193
- - More data types i.e. wifi, passkey etc.
174
+ ### Roadmap
175
+ - Color gradients for outer and inner eyes
194
176
  - Image background and shape improvements
195
177
  - More default component selections
data/Rakefile CHANGED
@@ -7,6 +7,8 @@ RSpec::Core::RakeTask.new(:spec)
7
7
 
8
8
  require "rubocop/rake_task"
9
9
 
10
- RuboCop::RakeTask.new
10
+ RuboCop::RakeTask.new do |task|
11
+ task.fail_on_error = false
12
+ end
11
13
 
12
14
  task default: %i[spec rubocop]
@@ -23,7 +23,7 @@ module QrForge
23
23
  cx: cx,
24
24
  cy: cy,
25
25
  r: r,
26
- 'stroke-width': stroke_width,
26
+ "stroke-width": stroke_width,
27
27
  stroke: color,
28
28
  fill: "transparent",
29
29
  test_id: @test_id
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "vips"
4
-
5
3
  module QrForge
6
4
  #
7
5
  # Handles exporting the generated QR code in various formats.
@@ -10,12 +8,15 @@ module QrForge
10
8
  @format = config.dig(:output, :format) || :svg
11
9
  end
12
10
 
11
+ #
12
+ # Exports the generated QR code in the specified format.
13
+ # @param svg [String] The generated SVG content of the QR code
14
+ # @return [String] The exported QR code in the specified format
15
+ # @raise [RuntimeError] if the format is unsupported
13
16
  def export(svg)
14
17
  case @format
15
18
  when :svg
16
- svg
17
- when :png
18
- as_png(svg)
19
+ process_svg(svg)
19
20
  else
20
21
  raise "Unsupported export format: #{@format}"
21
22
  end
@@ -24,15 +25,23 @@ module QrForge
24
25
  private
25
26
 
26
27
  #
27
- # Exports the SVG to PNG format using Vips.
28
- # @param svg [String] The SVG content to convert
29
- # @return [StringIO] A StringIO object containing the PNG data
30
- def as_png(svg)
31
- image = Vips::Image.svgload_buffer(svg)
32
- # TODO: Compression doesn't really seem to give much noticeable difference in quality or file size.
33
- buffer = image.write_to_buffer(".png")
28
+ # Performs any last-minute processing on the svg before exporting if applicable
29
+ # @param [String] svg the preprocessed svg
30
+ # @return [String] the final svg
31
+ def process_svg(svg)
32
+ return svg if ENV["SVG_OUTPUT_MODE"] == "test"
34
33
 
35
- StringIO.new(buffer)
34
+ clean(svg)
35
+ end
36
+
37
+ #
38
+ # Cleans the SVG by removing test_id attributes.
39
+ # @param svg [String] The generated SVG
40
+ # @return [String] The cleaned SVG content
41
+ def clean(svg)
42
+ doc = Nokogiri::XML(svg)
43
+ doc.xpath("//@test_id").remove
44
+ doc.to_xml
36
45
  end
37
46
  end
38
47
  end
@@ -20,7 +20,7 @@ module QrForge
20
20
  # - `:qr` - QR code specific settings (e.g., version).
21
21
  # - `:design` - Design specific settings (e.g., colors, shapes).
22
22
  # - `:output` - Export specific settings (e.g., format).
23
- # @return [String, StringIO] The SVG or PNG representation of the QR code
23
+ # @return [String] The SVG or PNG representation of the QR code
24
24
  def self.build(data:, type: :url, config: {})
25
25
  new(data:, type:, config:).build
26
26
  end
@@ -5,6 +5,7 @@ module QrForge
5
5
  # Payload is a factory class that builds different types of payloads based on the provided type and data.
6
6
  # It will validate the payload data (based on the type) and return a string representation of the payload.
7
7
  class Payload
8
+ # TODO: Add passkey and sms support and vcard
8
9
  PAYLOAD_TYPES = {
9
10
  wifi: ::QrForge::Payloads::Wifi,
10
11
  plain: ::QrForge::Payloads::PlainText,
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- puts "[Geo] file loaded"
4
-
5
3
  module QrForge
6
4
  module Payloads
7
5
  #
@@ -1,4 +1,4 @@
1
- require 'uri'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module QrForge
4
4
  module Payloads
@@ -6,7 +6,6 @@ module QrForge
6
6
  # Represents a telephone payload
7
7
  # @example return "https://example.com" or "http://example.com"
8
8
  class Phone
9
-
10
9
  def initialize(phone_number)
11
10
  @phone_number = phone_number
12
11
  end
@@ -1,4 +1,6 @@
1
- require 'uri'
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
2
4
 
3
5
  module QrForge
4
6
  module Payloads
@@ -6,7 +8,6 @@ module QrForge
6
8
  # Represents a URL payload
7
9
  # @example return "https://example.com" or "http://example.com"
8
10
  class Url
9
-
10
11
  def initialize(url)
11
12
  @url = url
12
13
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module QrForge
2
4
  module Payloads
3
5
  class PayloadValidationError < StandardError; end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rqrcode_core/qrcode"
3
+ require "rqrcode_core"
4
4
 
5
5
  module QrForge
6
6
  #
@@ -11,7 +11,7 @@ module QrForge
11
11
  attr_reader :modules, :version, :module_count, :quiet_zone
12
12
 
13
13
  def initialize(text:, version: 10, level: :h)
14
- qr = RQRCodeCore::QRCode.new(text, size: version, level: level)
14
+ qr = ::RQRCodeCore::QRCode.new(text, size: version, level: level)
15
15
  @version = qr.version
16
16
  @modules = qr.modules.map(&:dup)
17
17
  @module_count = @modules.size
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "nokogiri"
4
- require_relative "layout"
5
4
 
6
5
  module QrForge
7
6
  #
@@ -47,8 +46,7 @@ module QrForge
47
46
  @quiet_zone = 4
48
47
  @module_count = qr_data.module_count
49
48
  @image = config.dig(:design, :image)
50
- @width = config.dig(:design, :size)
51
- @height = config.dig(:design, :size)
49
+ @size = config.dig(:output, :size)
52
50
  @colors = DEFAULT_COLORS.merge(config.dig(:design, :colors) || {})
53
51
  @layout = QrForge::Layout.new(qr_data:, has_image: image_present?)
54
52
  end
@@ -58,10 +56,11 @@ module QrForge
58
56
  def to_svg
59
57
  Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
60
58
  xml.svg(
61
- width: @width || canvas_size,
62
- height: @height || canvas_size,
59
+ width: @size,
60
+ height: @size,
63
61
  xmlns: "http://www.w3.org/2000/svg",
64
- viewBox: "0 0 #{canvas_size} #{canvas_size}"
62
+ viewBox: "0 0 #{canvas_size} #{canvas_size}",
63
+ shape_rendering: "crispEdges"
65
64
  ) do
66
65
  draw_background(xml)
67
66
  draw_image(xml)
@@ -98,10 +97,10 @@ module QrForge
98
97
  def draw_image(xml)
99
98
  return unless image_present? && @qr_data.version >= 2
100
99
 
101
- image_range = @layout.image_area # a Range (row indices) for the image
100
+ image_range = @layout.image_area # row indices for the image
102
101
  size = image_range.size # width/height in modules
103
102
 
104
- # Convert module coords SVG coords (including quiet zone)
103
+ # Convert module coords to SVG coords (including quiet zone)
105
104
  x = image_range.first + @quiet_zone
106
105
  y = image_range.first + @quiet_zone
107
106
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QrForge
4
- VERSION = "1.1.0"
4
+ VERSION = "1.1.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qr_forge
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Keegankb93
@@ -51,20 +51,6 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '2.0'
54
- - !ruby/object:Gem::Dependency
55
- name: vips
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '8.15'
61
- type: :runtime
62
- prerelease: false
63
- version_requirements: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '8.15'
68
54
  - !ruby/object:Gem::Dependency
69
55
  name: zeitwerk
70
56
  requirement: !ruby/object:Gem::Requirement
@@ -133,6 +119,7 @@ metadata:
133
119
  homepage_uri: https://github.com/keegankb93/qr_forge
134
120
  source_code_uri: https://github.com/keegankb93/qr_forge
135
121
  changelog_uri: https://github.com/keegankb93/qr_forge/blob/main/CHANGELOG.md
122
+ rubygems_mfa_required: 'true'
136
123
  rdoc_options: []
137
124
  require_paths:
138
125
  - lib