【Ruby】cairoで画像作成を実装する(画像にスタンプと文字を追加する)
Railsアプリケーションにおいて、バックエンド側で以下のような画像作成をしたいと思い、サンプルアプリケーションを作ってみたので備忘録として残しておく。

ラジオ体操のスタンプみたいw
- Ruby : v3.1.2
- Ruby on Rails : v7.0.3.1
Ruby/Railsでどのライブラリを使うか
画像編集をしたいと思った場合、以下のライブラリが見つかった。
- RMagick(ImageMagickを扱えるライブラリ)
- rcairo(cairoを扱えるライブラリ)
画像編集ソフトであるImageMagickは本当に色々な編集が可能である。今回の既存の画像にスタンプを追加するぐらいであれば、ベクター画像処理で十分であると判断し、rcairoを選定することとした。
cairoとは
cairo は 2 次元ベクター画像を描画するためのライブラリである。
使い方としては、SVG(Scalable Vector Graphics)によく似ている感覚であった。(ベクター形式なので当たり前かw)
Docker設定
今回、Rails環境をDocker(Alpine Linux)で構築していたため、cairoをDocker imageにインストールすることにした。
cairo-devが用意されていたので、その他必要なパッケージも含め以下の通りインストールを行なった。
RUN apk add --update --no-cache \ make \ g++ \ jpeg-dev \ cairo-dev \ giflib-dev \ pango-dev \ libtool \ autoconf \ automake \ font-noto-cjk
文字の描画で日本語フォントが必要だったため、「font-noto-cjk」を追加した。
Rails設定
Ruby で cairo を使うには rcairo を使う必要がある。rcairo は Ruby と cairo のインターフェースである。
gem 'cairo'
Gemfileに上記を追記して、bundle installを行う。
今回、画像作成処理に関してはRakeタスクで実行することを想定した。
$ bundle exec rails g task calender_image
lib/tasks配下にcalender_image.rakeというファイルが生成される。
画像作成(描画処理)
スタンプを追加する下地の画像は一時的に以下のディレクトリに入れておいた。
lib/
└── tasks/
├── calender_image.rake
└── img/
└── calender_Aug.png
実際に作成したコードは以下の通りである。今回は、カレンダー画像の8/1の部分に星形のスタンプを追加し、カレンダーの下部にメッセージを追加した。
namespace :calender_image do desc "create calender image" task :create do open("#{Rails.root}/lib/tasks/img/calender-add-stamp.png", 'wb') do |output| open("#{Rails.root}/lib/tasks/img/calender_Aug.png", 'rb') do |input| new_image = draw_image(input, "継続は力なり。今年の夏も成長できる、ステキな夏になりますように。") output.write(new_image.read) end end end end def draw_image(io, message) surface = Cairo::ImageSurface.from_png(io) context = Cairo::Context.new(surface) # 星の座標 star_left_top_x = 187 star_left_top_y = 257 context.set_source_color(Cairo::Color.parse("#FFC700")) # 星の色設定 context.set_line_width(5) # 星の線幅設定 star_points = [ [ 54.32, 0 ], [ 71.09, 34 ], [ 108.63, 39.46 ], [ 81.47, 65.92 ], [ 87.89, 103.3 ], [ 54.32, 85.67 ], [ 20.74, 103.32 ], [ 27.16, 65.94 ], [ 0, 39.46 ], [ 37.54, 34 ], [ 54.32, 0 ] ] # 星の描画処理 context.move_to(star_left_top_x + star_points[0][0], star_left_top_y) for i in 0..10 do context.line_to(star_points[i][0] + star_left_top_x, star_points[i][1] + star_left_top_y) end context.stroke context.set_source_color(Cairo::Color.parse("#000000")) context.select_font_face("Noto Sans CJK JP", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_NORMAL) context.set_font_size(26) context.move_to(40,810) context.show_text("今日の一言") context.select_font_face("Noto Sans CJK JP", Cairo::FONT_SLANT_NORMAL, Cairo::FONT_WEIGHT_BOLD) context.move_to(70,860) context.show_text("#{message}") io = StringIO.new surface.write_to_png(io) io.pos = 0 io end
以下のコマンドを実行することで、Rakeタスクを動かすことができ、実際に画像作成を行うことができる。
$ bundle exec rails calender_image:create

rcairoの各種メソッドは、rcairoのGitHubのDocを参考にしてほしい。(日本語の説明もあって感謝しかない。)
画像描画の流れは以下の通りである。
(注意事項)
- 座標系は、左上を(0,0)とする
- コンテキストについても、座標系は左上が原点(0,0)となる
まとめ
cairoを使って画像作成処理を簡単に実装することができた。
あとは、スタンプの位置を日付ごとにどの位置とすべきか算出するロジックを追加することで、任意の日付にスタンプを追加することができる予定である。