Create A Qr Code With A Logo
QR codes are a great way to share links or information easily with your users, unless you’re a restaurant, in which case you should really consider a paper menu please.
In all seriousness, it’s a great way to easily let people scan and access specific information.
To say QR codes are a dime a dozen would be an understatement. Creating a composite QR code with a logo is a great way to help your code stand out and add some additional branding and feel to your app.
Being able to generate a custom QR code overlaid with a logo, while still being scannable is pretty easy with Rails.
This post will walk through setting up a simple standalone QR with logo app.
We’ll be using MiniMagick and RQRCode to create a PNG of the qr code, then create a composite image with the logo.
If you’d like to see everything in action, I have a video that goes through things in a little more detail
Getting started
The app is going to have a single model QRCode
.
It will have a string field for the URL and 3 attached active storage images.
3 might be a bit overkill but this will store the logo uploaded by a user, the QR code without the logo and finally the combined QR code image with the logo.
Create a new Rails app with Tailwind by running the following command in your terminal.
rails new qr_code_generator --css=tailwind
We’ll keep things simple by scaffolding the QrCode
resources
bin/rails generate scaffold QrCode url:string
Since this is a simple app, we’ll set the root route to the QrCode
index page.
# config/routes.rb
root "qr_codes#index"
Active Storage and Image Processing
With a simple model for the QR code, the next step is to add the image processing gem and install active storage for the images that will be attached.
If you look in the Gemfile
, you should already see the image_processing
gem in your Gemfile
# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
# gem "image_processing", "~> 1.2"
You can uncomment the gem and run bundle install
With the image processing gem installed and ready to go, the next step is to install Active Storage and run the corresponding migrations.
bin/rails active_storage:install
bin/rails db:migrate
With Active Storage installed, we can add our attachments we’ll be using.
app/models/qr_code.rb
class QrCode < ApplicationRecord
has_one_attached :logo
has_one_attached :original_image
has_one_attached :combined_image
end
We’ll be generating the combined_image
and the original_image
based on inputs from the user, including a logo
file.
Updating the form and adding the file attributes to the params in the controller will allow us to start storing the uploaded logo and URL attached to the QR code.
Add the file input for the logo to the form.
I’m also limiting the file type to PNG for simplicity. Or QR codes are going to be PNGs, so it makes sense to limit the logo to the same format. Accepting PNGs with transparent backgrounds also makes it easier to overlay the logo on the QR code after some processing.
This is by no means a foolproof way to ensure the file is a PNG, but it’s a good start. If you find yourself needing more, you can look into validating the file type in the model before saving.
app/views/qr_codes/_form.html.erb
<div class="my-5">
<%= form.label :logo %>
<%= form.file_field :logo, accept: "image/png" %>
</div>
Add logo
to the strong params in the controller.
app/controllers/qr_codes_controller.rb
def qr_code_params
params.require(:qr_code).permit(:url, :logo)
end
Adding the logo to the show page will give us an easy way to reference the image we uploaded.
app/views/qr_codes/_qr_code.html.erb
<div id="<%= dom_id qr_code %>">
<p class="my-5">
<strong class="block font-medium mb-1">Url:</strong>
<%= qr_code.url %>
<%= image_tag qr_code.combined_image %>
</p>
</div>
If you haven’t started the server yet, now would be a good time to start everything up and make sure it’s working.
If things are good to go, you should land directly on the QrCode
index page.
Clicking the New QR Code button, filling out the URL, and uploading a logo should create a new QR code record with the URL and logo attached.
QR codes.
We’ll be using the rqrcode gem to generate QR codes as a PNG and saving as an Active Storage attachment.
Add it to the Gemfile with::
gem "rqrcode", "~> 2.0"
and install the gem with bundle install
To keep a lot of the image processing logic out of the controller, we’ll create a method on the model to generate the QR code and save it as an attachment.
app/models/qr_code.rb
def generate_qr_code!
qr = RQRCode::QRCode.new(url)
original_qr = qr.as_png(size: 400,
bit_depth: 1,
border_modules: 4,
color_mode: ChunkyPNG::COLOR_GRAYSCALE,
color: "black",
file: nil,
fill: "white")
original_image.attach(io: StringIO.new(original_qr.to_s),
filename: "original_qr.png",
content_type: "image/png")
save!
end
This method will generate a QR code based on the URL and save it as an attachment.
Another advantage of having the QR code generation on the model is that we can test and diagnose issues in our Rails console without needing to go through the controller.
Running qr_code.generate_qr_code!
in the console (or anywhere) should generate a QR code from the URL and save it as an attachment.
Calling this method after we’ve successfully saved the QrCode
record will create the QR code and save it as an attachment.
app/controllers/qr_codes_controller.rb
def create
@qr_code = QrCode.new(qr_code_params)
respond_to do |format|
if @qr_code.save
@qr_code.generate_qr_code!
format.html { redirect_to qr_code_url(@qr_code), notice: "Qr code was successfully created." }
format.json { render :show, status: :created, location: @qr_code }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @qr_code.errors, status: :unprocessable_entity }
end
end
end
So we can make sure the image was created an attached, we can display it on the details page.
<%= image_tag qr_code.original_image %>
app/views/qr_codes/_qr_code.html.erb
<div id="<%= dom_id qr_code %>">
<p class="my-5">
<strong class="block font-medium mb-1">Url:</strong>
<%= qr_code.url %>
<%= image_tag qr_code.logo %>
<%= image_tag qr_code.original_image %>
</p>
</div>
If everything is working as expected, you should see the QR code image on the show page for the QR code record. To check to make sure everything is working as expected, you can call generate_qr_code!
on an existing record in the Rails console or create a new record through the form. (Screenshot of the QR code on the show page - Use real URLs)
Going Further
Adding a bit of flair to things, we’re ready to start combining the uploaded logo, this will be the combined_image
attachment.
At this point, there are 2 attached image to the QR code model, original_image
and logo
.
We’re now going to use MiniMagick to do a bit of formatting and create a composite image.
The combined logo will be using the same approach as the original QR code, meaning this code will be added to a method in the QrCode
model.
To start, we create a MiniMagick::Image
for the logo
and original_image
qr code.
Before attempting to combine the images, we’ll need to do some processing on the logo image. This will include resizing the image and adding a white background to make the logo stand out from the QR code.
Since we’re going to be doing some image processing, we’ll need to require the mini_magick
gem in the QrCode
model.
First, we create a MiniMagick::Image
object for the logo image.
logo_image = MiniMagick::Image.open(logo)
Then we do the same for the QR code image.
qr_code_image = MiniMagick::Image.open(original_image)
Next, we’ll resize the logo image and add a white background to help the logo stand out.
updated_logo_image = ImageProcessing::MiniMagick.source(logo_image)
.resize_and_pad(100, 100, background: "white").call
After creating the updated logo image, we can create a composite image with the QR code and logo.
composite_qr_code = qr_code_image.composite(updated_logo_image) do |c|
c.compose "Over"
c.gravity "center"
c.colorspace "sRGB"
end
The composite
method is used to overlay the updated logo image on top of the QR code image. The compose
option is set to “Over” to overlay the logo on top of the QR code. The gravity
option is set to “center” to center the logo on the QR code. The colorspace
option is set to “sRGB” to ensure the colors are displayed correctly.
Finally, we’ll attach the composite image to the combined_image
attachment.
qr_code.combined_image.attach(filename: "combined_qr_code.png",
io: File.open(composite_qr_code),
content_type: "image/png")
Then create the composite image and stores it as an Active Storage attachment.
Here’s what the final method looks like in the QrCode
model.
def generate_combined_image!
logo_image = MiniMagick::Image.open(logo)
qr_code_image = MiniMagick::Image.open(original_image)
updated_logo_image = ImageProcessing::MiniMagick.source(logo_image).resize_and_pad(100, 100, background: "white").call
composite_qr_code = qr_code_image.composite(updated_logo_image) do |c|
c.compose "Over"
c.gravity "center"
c.colorspace "sRGB"
end
combined_image.attach(filename: "combined_qr_code.png",
io: File.open(composite_qr_code.path),
content_type: "image/png")
save!
end
If you’d like to break this down into smaller steps, you can call each method individually in the Rails console. Here’s an example of how you could call each method individually in the Rails console to create the composite image and save it as an attachment.
# in the Rails console
require "mini_magick"
qr_code = QrCode.last
logo_image = MiniMagick::Image.open(qr_code.logo)
qr_code_image = MiniMagick::Image.open(qr_code.original_image)
updated_logo_image = ImageProcessing::MiniMagick.source(logo_image)
.resize_and_pad(100, 100, background: "white").call
composite_qr_code = qr_code_image.composite(updated_logo_image) do |c|
c.compose "Over"
c.gravity "center"
c.colorspace "sRGB"
end
qr_code.combined_image.attach(filename: "combined_qr_code.png",
io: File.open(composite_qr_code.path),
content_type: "image/png")
qr_code.save!
In the same way we called generate_qr_code!
after saving the record, we can call generate_combined_image!
to create the composite image with the logo from the Rails console on an existing record that has a logo and QR code attached.
The last step is to call the generate_combined_image!
method after the QR code has been generated and saved.
app/controllers/qr_codes_controller.rb
def create
@qr_code = QrCode.new(qr_code_params)
respond_to do |format|
if @qr_code.save
@qr_code.generate_qr_code!
@qr_code.generate_combined_image!
format.html { redirect_to qr_code_url(@qr_code), notice: "Qr code was successfully created." }
format.json { render :show, status: :created, location: @qr_code }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @qr_code.errors, status: :unprocessable_entity }
end
end
end
We’ll also include this in the update action so that the combined image is updated whenever the QR code is updated.
def update
respond_to do |format|
if @qr_code.update(qr_code_params)
@qr_code.generate_qr_code!
@qr_code.generate_combined_image!
format.html { redirect_to qr_code_url(@qr_code), notice: "Qr code was successfully updated." }
format.json { render :show, status: :ok, location: @qr_code }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @qr_code.errors, status: :unprocessable_entity }
end
end
end
Display the combined image on the show page for the QR code record to see the final result.
<%= image_tag qr_code.combined_image %>
app/views/qr_codes/_qr_code.html.erb
<div id="<%= dom_id qr_code %>">
<p class="my-5">
<strong class="block font-medium mb-1">Url:</strong>
<%= qr_code.url %>
<%= image_tag qr_code.logo %>
<%= image_tag qr_code.original_image %>
<%= image_tag qr_code.combined_image %>
</p>
</div>
If all goes as planned, you should be re-directed to the show page for the newly created QR Code record and see if all the logos are correct.
And with that, you’ve created a simple tool too add some style and flair to your QR codes.
This is a pretty simple example and there are a lot of ways to improve and expand on this feature. You could add more options for the QR code generation, add more image processing options, or even add a background color to the QR code for some additional flair.
You can view the full code for this example on GitHub.