Ruby/Railsで画像に文字を入れ込む 


こんにちは。エンジニアのronです。

SmartHRは、Rubykaigi 2021 Takeoutでスケジュールアプリを提供しました。
https://tech.smarthr.jp/entry/2021/11/02/204403

実はその中で、スケジュールページで設定した一言コメントがOGPに反映されるようにしていました。
これは、Twitterで自分の視聴予定をシェアしてワイワイ盛り上がってほしいという気持ちから実装したものです。

![スクリーンショット 2021-12-18 13.56.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87285/a9252262-b996-5e2d-08cc-9761190ac618.png)

今回は、どうやって画像の生成を行っているのかを解説していきたいと思います。


## 画像の生成方法
Rubyで画像を生成する方法はいくつかあります。
- imagemagick
- Thinreports
- wkhtmltoimage
などが候補になりますが、今回は改行などレイアウトの簡単さから、wkhtmltoimageを使うことにしました。

## 実際に生成する部分のコード
https://github.com/kufu/mie/blob/main/app/controllers/plans/ogps_controller.rb
こちらが、OGPの生成を行っているコントローラーのソースコードです。

``` ruby
class Plans::OgpsController < ApplicationController
  before_action :set_plan

  def show
    send_data IMGKit.new(get_html(@plan.description), quality: 20, width: 800).to_img(:png), type: 'image/png', disposition: 'inline'
  end

  private
  def set_plan
    @plan = Plan.find(params[:plan_id])
  end

  def get_html(body)
    <<~HTML
    <!DOCTYPE html>
<html lang="ja">
  <head>
	<meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
	<style>
    *,*::before,*::after{box-sizing:border-box}body,h1,h2,h3,h4,p,figure,blockquote,dl,dd{margin:0}ul[role="list"],ol[role="list"]{list-style:none}html:focus-within{scroll-behavior:smooth}body{min-height:100vh;text-rendering:optimizeSpeed;line-height:1.5}a:not([class]){text-decoration-skip-ink:auto}img,picture{max-width:100%;display:block}input,button,textarea,select{font:inherit}@media(prefers-reduced-motion:reduce){html:focus-within{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important;scroll-behavior:auto !important}}
	  @charset "UTF-8";
	  html {
      font-family: 'Noto Sans CJK JP', 'Noto Color Emoji', 'Noto Emoji', sans-serif;
		  -ms-text-size-adjust: 100%;
		  -webkit-text-size-adjust: 100%;
	  }
    * {
      box-sizing: border-box;
    }
    .ogp-frame {
		  width: 1200px;
      height: 630px;
      overflow: hidden;
      background: no-repeat center url(data:image/png;#{ogp_background_image});
      padding: 44px 34px 240px 64px;
      line-height: 1.5;
      font-size: 58px;
      color: white;
      word-break: break-all;
    }
    .ogp-body {
      width: 100%;
      height: 100%;
      overflow-y: hidden;
      display: -webkit-box;
      -webkit-line-clamp: 4;
      -webkit-box-orient: vertical;
      padding: 0 10px 0 0;
    }
    
    .ogp-body::-webkit-scrollbar {
        display:none;
    }
	  
	</style>
  </head>
  <body><div class="ogp-frame"><div class="ogp-body">#{ERB::Util.h(body)}</div></div></body></html>
    HTML
  end

  def ogp_background_image
    # data-url
  end
end
```

wkhtmltoimageをrubyで扱うことができるIMGKitというGemを使用しています。使い方は非常に簡単で、
`IMGKit.new`の引数にHTML文字列(と、オプション)を渡し、`to_image`を呼ぶことで、画像を得ることができます。

https://github.com/csquared/IMGKit


HTML自体は、背景画像を`background-image`にして、その上に文字を表示しているだけのシンプルなものです。
overflowやwebkit-line-clampを使用して、文字が背景からはみ出すのを防止したり、長すぎる文章の場合は4行までしか表示しないようにしたりしています。

## おわりに
画像に文字列を埋め込んでレイアウトすることはかなり簡単にできるということが伝われば幸いです。
OGPに限らず、アイディア次第で様々なサービスで利用できると思います。
面白いコラ画像ジェネレーターを作ったらぜひご一報ください！