Introduction
Creating user avatars is a pretty simple thing. Gems like avatarly or letter_avatar can generate PNG images with user initials. This time I would like to show you a different approach - SVG generated by the standard ruby ERB template engine.
Why?
There are some advantages of using SVG instead of PNG in this case:
- image generation is faster
- it doesn’t require any additional dependencies like imagemagick
- SVGs scale better on high resolution / high pixel ratio devices
SVG images in <img>
are widely supported in the browsers - https://caniuse.com/svg-img.
Although it has very limited support if it comes to email clients - https://www.caniemail.com/features/image-svg/.
So if you need to place avatars inside emails I wouldn’t recommend this solution.
Implementation
Let’s start with our controller. We want our URL to look like this: /users/:user_id/avatar.svg
.
If you already have a users controller you can add it as a method there or add a nested resource:
# config/routes.rb
resources :users do
resource :avatar, only: :show
end
In my example, I will use user initials generated by taking first letters from two first words from the user’s full name.
# app/controllers/avatars_controller.rb
class AvatarsController < ::ApplicationController
before_action :set_user
def show
@letters = @user.full_name.split.map(&:first).join.upcase[0..1]
end
private
def set_user
@user = User.find(params[:user_id])
end
end
Now we can create a view for our action with .svg.erb
extension.
Let’s start with a simple image with red background and white letters.
app/views/avatars/show.svg.erb
:
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
<rect width="100%" height="100%" fill="#c5523f"/>
<text fill="#fff" font-family="Helvetica,Arial,sans-serif" font-size="26" font-weight="500" x="50%" y="55%" dominant-baseline="middle" text-anchor="middle">
<%= @letters %>
</text>
</svg>
Result:
Our text is centered thanks to these attributes: x="50%" y="55%" dominant-baseline="middle" text-anchor="middle"
That was pretty simple, right? Now let’s add more colors. We can pick color for each user using id
.
# app/controllers/avatars_controller.rb
class AvatarsController < ::ApplicationController
COLORS = %w(#f2b736 #c5523f #499255 #1875e5).freeze
before_action :set_user
def show
@letters = @user.full_name.split.map(&:first).join.upcase[0..1]
@background = COLORS[@user.id % COLORS.length]
end
private
def set_user
@user = User.find(params[:user_id])
end
end
Our updated view:
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
<rect width="100%" height="100%" fill="<%= @background %>"/>
<text fill="#fff" font-family="Helvetica,Arial,sans-serif" font-size="26" font-weight="500" x="50%" y="55%" dominant-baseline="middle" text-anchor="middle">
<%= @letters %>
</text>
</svg>
Results:
Alright, now we have simple user avatars ready to use! But what if we would like to use some non-standard font?
Unfortunately, things get complicated here. In theory, we can add a <style>
block inside the SVG file and load a font from Google Fonts or any other place.
It will work if you open the file directly in the browser but it won’t if you link to it via <img>
tag (browser will ignore external import).
Don’t worry, there is a way to make it work - we just need to place base64-encoded font inside SVG.
In our controller we will add additional const with an encoded font (since we don’t want to do it on each request, just once during initialization):
# app/controllers/avatars_controller.rb
class AvatarsController < ::ApplicationController
COLORS = %w(#f2b736 #c5523f #499255 #1875e5).freeze
ENCODED_FONT = Base64.encode64(Rails.root.join('app/assets/fonts/OpenSans-SemiBold.ttf').read)
.delete("\n").freeze
before_action :set_user
def show
@letters = @user.full_name.split.map(&:first).join.upcase[0..1]
@background = COLORS[@user.id % COLORS.length]
end
private
def set_user
@user = User.find(params[:user_id])
end
end
And update our view:
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
<style>
@font-face {
font-family: "OpenSans";
font-weight: 500;
src: url(data:application/x-font-ttf;base64,<%= ::AvatarsController::ENCODED_FONT %>);
}
</style>
<rect width="100%" height="100%" fill="<%= @background %>"/>
<text fill="#fff" font-family="OpenSans,Arial,sans-serif" font-size="26" font-weight="500" x="50%" y="55%" dominant-baseline="middle" text-anchor="middle">
<%= @letters %>
</text>
</svg>
And it’s done! Our final result:
Conclusion
Creating SVG using ERB is really easy since it’s basically an XML file. With the solution above we are able to create simple user avatars without any additional libraries. If you have any questions, don’t hesitate to ask in the comments below.