qr_forge 1.0.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 +4 -4
- data/.rubocop.yml +5 -1
- data/README.md +158 -20
- data/Rakefile +3 -1
- data/lib/qr_forge/components/eye_outer/circle.rb +1 -1
- data/lib/qr_forge/exporter.rb +22 -13
- data/lib/qr_forge/forge.rb +11 -7
- data/lib/qr_forge/payload.rb +40 -0
- data/lib/qr_forge/payloads/geo.rb +27 -0
- data/lib/qr_forge/payloads/phone.rb +34 -0
- data/lib/qr_forge/payloads/plain_text.rb +25 -0
- data/lib/qr_forge/payloads/url.rb +32 -0
- data/lib/qr_forge/payloads/wifi.rb +102 -0
- data/lib/qr_forge/payloads.rb +7 -0
- data/lib/qr_forge/qr_data.rb +2 -2
- data/lib/qr_forge/renderer.rb +7 -8
- data/lib/qr_forge/version.rb +1 -1
- data/lib/qr_forge.rb +12 -3
- metadata +9 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68e6f3102dc3d6954c036f6bfb17a2820743956eea34b7ef11565f173cab0a9a
|
4
|
+
data.tar.gz: e3d5a5c12559c0cf707537ce5163d10717e0f1c18a39130cfb5ba940bc71647d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,39 +1,177 @@
|
|
1
|
-
|
1
|
+

|
2
2
|
|
3
|
-
|
3
|
+
An example of the output of QR Forge
|
4
4
|
|
5
|
-
|
5
|
+
# QR Forge
|
6
6
|
|
7
|
-
|
7
|
+
This library is a QR Code renderer that lets you customize almost every aspect of a QR Code. From module design to a logo, this library is meant to be design-extensible, which means you can render your own components such as modules, the outer eyes and inner eyes etc. in your own custom SVG shapes.
|
8
8
|
|
9
|
-
|
9
|
+
# Installation
|
10
10
|
|
11
|
-
|
11
|
+
```shell
|
12
|
+
gem install qr_forge
|
13
|
+
```
|
14
|
+
|
15
|
+
or if using bundler
|
16
|
+
|
17
|
+
```shell
|
18
|
+
bundle add qr_forge
|
19
|
+
```
|
20
|
+
|
21
|
+
# Usage
|
22
|
+
|
23
|
+
To create a QR Code you can simply do the following:
|
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
|
+
|
31
|
+
```ruby
|
32
|
+
QrForge::Forge.build(data: "https://yourlinkhere.com", type: :plain)
|
33
|
+
```
|
34
|
+
|
35
|
+
#### URL
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
QrForge::Forge.build(data: "https://yourlinkhere.com", type: :url)
|
39
|
+
```
|
40
|
+
|
41
|
+
#### Wifi
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
QrForge::Payload.build(data: { ssid: "MyNetwork", password: "MyPassword", encryption: "WPA" }, type: :wifi)
|
45
|
+
```
|
46
|
+
Fields:
|
47
|
+
- ssid,
|
48
|
+
- password
|
49
|
+
- encryption (WEP WPA WPA2 WPA3)
|
50
|
+
- hidden (true/false)
|
51
|
+
|
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
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
QrForge::Payload.build(data: { latitude: 40.712776, longitude: -74.005974 }, type: :geo)
|
59
|
+
```
|
60
|
+
Fields:
|
61
|
+
- latitude
|
62
|
+
- longitude
|
63
|
+
|
64
|
+
#### Phone number
|
65
|
+
|
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
|
+
```
|
73
|
+
|
74
|
+
More are planned to be added.
|
75
|
+
|
76
|
+
### Design
|
12
77
|
|
13
|
-
|
14
|
-
|
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.
|
79
|
+
|
80
|
+
##### QR
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
QrForge::Payload.build(..., design: { qr { version: 10 } }, ...)
|
15
84
|
```
|
85
|
+
Fields:
|
86
|
+
- Version
|
87
|
+
|
88
|
+
##### Image
|
16
89
|
|
17
|
-
|
90
|
+
```ruby
|
91
|
+
QrForge::Payload.build(..., design: { image: "..." }, ...)
|
92
|
+
```
|
93
|
+
The image should be a **Base64 encoded** image like the following:
|
18
94
|
|
19
|
-
```
|
20
|
-
|
95
|
+
```ruby
|
96
|
+
Base64.strict_encode64(image_png_binary)
|
21
97
|
```
|
98
|
+
More image formats will be added as needed or required.
|
99
|
+
|
100
|
+
##### Colors
|
22
101
|
|
23
|
-
|
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.
|
24
111
|
|
25
|
-
|
112
|
+
See the Components section for more details on which field references what component of the QR Code.
|
26
113
|
|
27
|
-
|
114
|
+
### Output
|
28
115
|
|
29
|
-
|
116
|
+
```ruby
|
117
|
+
QrForge::Payload.build(..., output: { size: 250 }, ...)
|
118
|
+
```
|
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.
|
30
123
|
|
31
|
-
|
124
|
+
There is only an ouput of SVG format, see this PR for reasoning: https://github.com/keegankb93/qr_forge/pull/1
|
32
125
|
|
33
|
-
|
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.
|
34
127
|
|
35
|
-
|
128
|
+
### Components
|
36
129
|
|
37
|
-
|
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
|
+
...)
|
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" />
|
141
|
+
|
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" />
|
144
|
+
|
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" />
|
147
|
+
|
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
|
154
|
+
|
155
|
+
This is the code that was used to create the QR Code at the top of the page.
|
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
|
+
```
|
38
173
|
|
39
|
-
|
174
|
+
### Roadmap
|
175
|
+
- Color gradients for outer and inner eyes
|
176
|
+
- Image background and shape improvements
|
177
|
+
- More default component selections
|
data/Rakefile
CHANGED
data/lib/qr_forge/exporter.rb
CHANGED
@@ -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
|
-
#
|
28
|
-
# @param
|
29
|
-
# @return [
|
30
|
-
def
|
31
|
-
|
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
|
-
|
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
|
data/lib/qr_forge/forge.rb
CHANGED
@@ -4,21 +4,25 @@ module QrForge
|
|
4
4
|
#
|
5
5
|
# Entry point for building QRCodes
|
6
6
|
class Forge
|
7
|
-
def initialize(
|
7
|
+
def initialize(data:, type:, config:)
|
8
8
|
version = config.dig(:qr, :version)
|
9
9
|
|
10
|
-
@data = QrForge::QrData.new(text:, version:)
|
10
|
+
@data = QrForge::QrData.new(text: QrForge::Payload.build(data:, type:).to_s, version:)
|
11
11
|
@renderer = QrForge::Renderer.new(qr_data: @data, config:)
|
12
12
|
@exporter = QrForge::Exporter.new(config:)
|
13
13
|
end
|
14
14
|
|
15
15
|
#
|
16
16
|
# Builds a QR code with the given parameters.
|
17
|
-
# @param
|
18
|
-
# @param
|
19
|
-
# @
|
20
|
-
|
21
|
-
|
17
|
+
# @param data [String, Hash] The data to encode in the QR code. This can be a string or a hash for specific payloads.
|
18
|
+
# @param type [Symbol] The type of QR code to build (e.g., :plain, :url)
|
19
|
+
# @param config [Hash] Configuration options for the QR code, including design and export settings.
|
20
|
+
# - `:qr` - QR code specific settings (e.g., version).
|
21
|
+
# - `:design` - Design specific settings (e.g., colors, shapes).
|
22
|
+
# - `:output` - Export specific settings (e.g., format).
|
23
|
+
# @return [String] The SVG or PNG representation of the QR code
|
24
|
+
def self.build(data:, type: :url, config: {})
|
25
|
+
new(data:, type:, config:).build
|
22
26
|
end
|
23
27
|
|
24
28
|
def build
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QrForge
|
4
|
+
#
|
5
|
+
# Payload is a factory class that builds different types of payloads based on the provided type and data.
|
6
|
+
# It will validate the payload data (based on the type) and return a string representation of the payload.
|
7
|
+
class Payload
|
8
|
+
# TODO: Add passkey and sms support and vcard
|
9
|
+
PAYLOAD_TYPES = {
|
10
|
+
wifi: ::QrForge::Payloads::Wifi,
|
11
|
+
plain: ::QrForge::Payloads::PlainText,
|
12
|
+
url: ::QrForge::Payloads::Url,
|
13
|
+
geo: ::QrForge::Payloads::Geo,
|
14
|
+
phone: ::QrForge::Payloads::Phone
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
#
|
18
|
+
# Builds a payload based on the type and data provided.
|
19
|
+
# @param type [Symbol] The type of payload to build (e.g., :url, :wifi).
|
20
|
+
# @param data [String, Hash] The data to encode in the payload.
|
21
|
+
# @return [String] The string representation of the payload.
|
22
|
+
def self.build(type:, data:)
|
23
|
+
klass = PAYLOAD_TYPES[type.to_sym]
|
24
|
+
|
25
|
+
raise ArgumentError "Invalid payload type: #{type}" unless klass
|
26
|
+
|
27
|
+
payload = case data
|
28
|
+
when Hash
|
29
|
+
klass.new(**data)
|
30
|
+
when String
|
31
|
+
klass.new(data)
|
32
|
+
else
|
33
|
+
raise ArgumentError, "Invalid data type: #{data.class}. Expected Hash or String."
|
34
|
+
end
|
35
|
+
|
36
|
+
payload.validate!
|
37
|
+
payload
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QrForge
|
4
|
+
module Payloads
|
5
|
+
#
|
6
|
+
# Represents a geo lat/long payload
|
7
|
+
# @example return "geo:40.712776,-74.005974"
|
8
|
+
class Geo
|
9
|
+
def initialize(latitude:, longitude:)
|
10
|
+
@latitude = latitude
|
11
|
+
@longitude = longitude
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"geo:#{@latitude},#{@longitude}"
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Validates that the passed latitude and longitude are within valid ranges.
|
20
|
+
def validate!
|
21
|
+
return if (-90..90).cover?(@latitude.to_f) && (-180..180).cover?(@longitude.to_f)
|
22
|
+
|
23
|
+
raise PayloadValidationError, "Latitude or longitude out of range"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QrForge
|
4
|
+
module Payloads
|
5
|
+
#
|
6
|
+
# Represents a telephone payload
|
7
|
+
# @example return "https://example.com" or "http://example.com"
|
8
|
+
class Phone
|
9
|
+
def initialize(phone_number)
|
10
|
+
@phone_number = phone_number
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
"tel:#{@phone_number}"
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Validates that the passed phone number is in a valid format.
|
19
|
+
# @example
|
20
|
+
# valid phones:
|
21
|
+
# +919367788755
|
22
|
+
# 8989829304
|
23
|
+
# +16308520397
|
24
|
+
# 786-307-3615
|
25
|
+
# 555.555.5555
|
26
|
+
def validate!
|
27
|
+
# credit: https://ihateregex.io/expr/phone/
|
28
|
+
return if @phone_number =~ /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/
|
29
|
+
|
30
|
+
raise PayloadValidationError, "Invalid phone number format"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QrForge
|
4
|
+
module Payloads
|
5
|
+
#
|
6
|
+
# Represents a plain text payload
|
7
|
+
# @example return "Hello, World!"
|
8
|
+
class PlainText
|
9
|
+
def initialize(text)
|
10
|
+
@text = text
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
@text
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Validates that the passed data is a string.
|
19
|
+
def validate!
|
20
|
+
raise PayloadValidationError, "Must be a valid string" unless @text.is_a?(String)
|
21
|
+
raise PayloadValidationError, "String cannot be empty" if @text.empty?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
module QrForge
|
6
|
+
module Payloads
|
7
|
+
#
|
8
|
+
# Represents a URL payload
|
9
|
+
# @example return "https://example.com" or "http://example.com"
|
10
|
+
class Url
|
11
|
+
def initialize(url)
|
12
|
+
@url = url
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@url
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Validates that the passed url is a valid HTTP or HTTPS URL.
|
21
|
+
def validate!
|
22
|
+
uri = URI.parse(@url)
|
23
|
+
|
24
|
+
unless uri.is_a?(::URI::HTTP) || uri.is_a?(::URI::HTTPS)
|
25
|
+
raise PayloadValidationError, "Must be a valid HTTP/HTTPS URL"
|
26
|
+
end
|
27
|
+
rescue ::URI::InvalidURIError
|
28
|
+
raise PayloadValidationError, "Invalid URL syntax"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QrForge
|
4
|
+
module Payloads
|
5
|
+
# Represents a Wi-Fi network payload
|
6
|
+
# @example returns WIFI:T:<encryption>;S:<ssid>;P:<password>;H:<hidden>;;
|
7
|
+
class Wifi
|
8
|
+
VALID_ENCRYPTIONS = %w[WEP WPA WPA2 WPA3].freeze
|
9
|
+
NO_PASSWORD_ENCRYPTION = "NOPASS"
|
10
|
+
|
11
|
+
def initialize(encryption:, ssid:, password: nil, hidden: false)
|
12
|
+
@encryption = encryption.upcase
|
13
|
+
@ssid = ssid
|
14
|
+
@password = password
|
15
|
+
@hidden = hidden
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Encryption type for the Wi-Fi network.
|
20
|
+
# @return [String, nil] The encryption type or nil if no password is set.
|
21
|
+
def encryption_type
|
22
|
+
"T:#{@encryption}" unless @encryption == NO_PASSWORD_ENCRYPTION
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# SSID of the Wi-Fi network.
|
27
|
+
# @return [String] The SSID
|
28
|
+
def ssid
|
29
|
+
"S:#{escape(@ssid)}"
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Password for the Wi-Fi network.
|
34
|
+
# @return [String, nil] The password or nil if no password is set.
|
35
|
+
def password
|
36
|
+
"P:#{escape(@password)}" if @password && @encryption != NO_PASSWORD_ENCRYPTION
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Indicates if the Wi-Fi network is hidden
|
41
|
+
# @return [String, nil] "H:true" if hidden, nil otherwise
|
42
|
+
def hidden
|
43
|
+
"H:true" unless @hidden.nil? || @hidden == false
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Returns the parts of the Wi-Fi payload as an array.
|
48
|
+
# @return [Array<String>] The parts of the Wi-Fi payload
|
49
|
+
def parts
|
50
|
+
parts = []
|
51
|
+
parts << ssid
|
52
|
+
parts << encryption_type
|
53
|
+
parts << password
|
54
|
+
parts << hidden
|
55
|
+
|
56
|
+
parts.compact
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
"WIFI:#{parts.join(";")};;"
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Validates the Wi-Fi payload data.
|
65
|
+
# @raise [PayloadValidationError] if the SSID is blank or if the password is required but not provided.
|
66
|
+
# @raise [PayloadValidationError] if the encryption type is invalid.
|
67
|
+
def validate!
|
68
|
+
raise PayloadValidationError, "SSID is required" if blank?(@ssid)
|
69
|
+
|
70
|
+
if @encryption != NO_PASSWORD_ENCRYPTION && blank?(@password)
|
71
|
+
raise PayloadValidationError, "Password is required for #{@encryption} networks"
|
72
|
+
end
|
73
|
+
|
74
|
+
return if VALID_ENCRYPTIONS.include?(@encryption) || @encryption == NO_PASSWORD_ENCRYPTION
|
75
|
+
|
76
|
+
raise PayloadValidationError, "Invalid encryption: #{@encryption}"
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
#
|
82
|
+
# Escapes special characters in the Wi-Fi payload.
|
83
|
+
# @param str [String, NilClass] The string to escape
|
84
|
+
# @return [String] The escaped string
|
85
|
+
def escape(str)
|
86
|
+
return "" if blank?(str)
|
87
|
+
|
88
|
+
# https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11
|
89
|
+
# Escape characters: \ ; , : " as they are valid in SSID but are used as delimiters in the payload format
|
90
|
+
str.to_s.gsub(/([\\;,:"])/) { "\\#{::Regexp.last_match(1)}" }
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Checks if a value is blank.
|
95
|
+
# @param val [String, nil] The value to check
|
96
|
+
# @return [Boolean] true if the value is blank, false otherwise
|
97
|
+
def blank?(val)
|
98
|
+
val.nil? || val.strip == ""
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/qr_forge/qr_data.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "rqrcode_core
|
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
|
data/lib/qr_forge/renderer.rb
CHANGED
@@ -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
|
-
@
|
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: @
|
62
|
-
height: @
|
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 #
|
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
|
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
|
|
data/lib/qr_forge/version.rb
CHANGED
data/lib/qr_forge.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "zeitwerk"
|
4
|
-
loader = Zeitwerk::Loader.for_gem
|
5
|
-
loader.setup
|
6
4
|
|
7
|
-
|
5
|
+
# Entry point for the QrForge gem.
|
6
|
+
module QrForge
|
7
|
+
def self.loader
|
8
|
+
@loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
|
9
|
+
loader.tag = "qr_forge"
|
10
|
+
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
11
|
+
loader.setup
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
loader
|
16
|
+
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.
|
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
|
@@ -116,6 +102,13 @@ files:
|
|
116
102
|
- lib/qr_forge/exporter.rb
|
117
103
|
- lib/qr_forge/forge.rb
|
118
104
|
- lib/qr_forge/layout.rb
|
105
|
+
- lib/qr_forge/payload.rb
|
106
|
+
- lib/qr_forge/payloads.rb
|
107
|
+
- lib/qr_forge/payloads/geo.rb
|
108
|
+
- lib/qr_forge/payloads/phone.rb
|
109
|
+
- lib/qr_forge/payloads/plain_text.rb
|
110
|
+
- lib/qr_forge/payloads/url.rb
|
111
|
+
- lib/qr_forge/payloads/wifi.rb
|
119
112
|
- lib/qr_forge/qr_data.rb
|
120
113
|
- lib/qr_forge/renderer.rb
|
121
114
|
- lib/qr_forge/version.rb
|
@@ -126,6 +119,7 @@ metadata:
|
|
126
119
|
homepage_uri: https://github.com/keegankb93/qr_forge
|
127
120
|
source_code_uri: https://github.com/keegankb93/qr_forge
|
128
121
|
changelog_uri: https://github.com/keegankb93/qr_forge/blob/main/CHANGELOG.md
|
122
|
+
rubygems_mfa_required: 'true'
|
129
123
|
rdoc_options: []
|
130
124
|
require_paths:
|
131
125
|
- lib
|