ActiveStorageのvariantを利用して画像に文字を重ね合わせる

# 概要
* 表題まま
* Imagemagickでできることなら、variantを使うとすっきりかけます

# 環境
* Rails 6 beta 1
* Docker

# コード
* https://github.com/junara/komee

# スクショ

[![Image from Gyazo](https://i.gyazo.com/f1e137b46b2cba4e35670f35806fef36.png)](https://gyazo.com/f1e137b46b2cba4e35670f35806fef36)

[![Image from Gyazo](https://i.gyazo.com/0557913c4ad4a4f50f19910d09760c8d.gif)](https://gyazo.com/0557913c4ad4a4f50f19910d09760c8d)

# 解説
ActiveStorageにはvariantという便利なメソッドがあります。これは、アップロードした画像をリサイズする事ができます。
たとえば、下記のようにimageにアップロードされる場合は、 `item.image.variant(resize: "100x100")` （[参考 rails guid](https://railsguides.jp/active_storage_overview.html#%E7%94%BB%E5%83%8F%E3%82%92%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B)） と書くと 100x100の大きさにリサイズできます。
また、variantで処理すると便利なのが processed。  `item.vaiant(resize: '100x100').processed` と書くと最初は動的にimageをリサイズします。しかし、2回目以降は、1回目のときに画像をキャッシュするので、画像の表示を効率的に行うことができます。（[参考](https://api.rubyonrails.org/classes/ActiveStorage/Variant.html#method-i-processed)）

```
class Item < ApplicationRecord
  has_one_attached :image
end
```

variantは、ImageMagickができることは何でもできます。具体的には、[こちら](https://www.imagemagick.org/script/mogrify.php) のページのオプションを組み合わせることでカスタムされたvariantを自作できます。

たとえば、今回は以下の箇所でつかっています。

```app/models/item.rb
# frozen_string_literal: true

class Item < ApplicationRecord
# 省略
  def resize_image(width = 500, height = 500)
    variation = ActiveStorage::Variation.new(
      Uploads.resize_to_fit(width: width, height: height, blob: image.blob, message: message)
    )
    ActiveStorage::Variant.new(image.blob, variation)
  end
end

```

```app/models/concerns/uploads.rb
# frozen_string_literal: true

# REF http://www.carlosramireziii.com/what-options-can-be-passed-to-the-active-storage-variant-method.html?utm_source=rubyflow
# REF https://prograils.com/posts/rails-5-2-active-storage-new-approach-to-file-uploads

class Uploads
  class << self
    def jpeg?(blob)
      blob.content_type.include? 'jpeg'
    end

    def optimize
      {
        strip: true
      }
    end

    def optimize_jpeg
      {
        strip: true,
        'sampling-factor' => '4:2:0',
        quality: '85',
        interlace: 'JPEG',
        colorspace: 'sRGB'
      }
    end

    def optimize_hash(blob)
      return optimize_jpeg if jpeg? blob

      optimize
    end

    def resize_to_fit(width: nil, height: nil, blob:, message:)
      n = 5
      pointsize = width / n
      num = n - 1
      fill = 'red'
      gravity = 'north'
      resize = resize(width: width, height: height)
      optimize_hash(blob).merge(resize: resize,
                                combine_options: { font: font, fill: fill, gravity: gravity, pointsize: pointsize,
                                                   draw: "text 0,#{pointsize / 5} '#{new_lined_message(message, num)}' " })
    end

    private

    def new_lined_message(message, length = 4)
      message.scan(/.{1,#{length}}/).join("\n")
    end

    def font
      # https://googlefonts.github.io/japanese/
      # '.fonts/FontopoNIKUKYU.otf'
      # '.fonts/ipaexg.ttf'
      '.fonts/nicomoji-plus_1.11.ttf'
    end

    def resize(width:, height:)
      if width && height
        "#{width}x#{height}"
      elsif width
        "#{width}x"
      else
        "x#{height}"
      end
    end
  end
end

```

`Uploads#resize_to_fit` の resize_to_fitがミソです。特にこの箇所が画像処理をしているところです。
fontでフォントを指定して、fillで文字色を規定して、gravityで文字を表示する場所を指定して、pointsizeで文字の大きさを指定します。
また、fontは、ここを見て[googleのwebからダウンロード](https://googlefonts.github.io/japanese/)して `.fonts` フォルダに入れました。
さらに、drawを使って画像に描画したい文字をしていします。[こちら](https://www.imagemagick.org/script/mogrify.php)で機能を照らし合わせるとわかりやすいです。


```
(resize: resize,
                                combine_options: { font: font, fill: fill, gravity: gravity, pointsize: pointsize,
                                                   draw: "text 0,#{pointsize / 5} '#{new_lined_message(message, num)}' " }
```

これを実際に使う場合は以下の通り。

```
<%= image_tag item.resize_image(300, nil).processed %>
```

# 参考
* http://www.carlosramireziii.com/what-options-can-be-passed-to-the-active-storage-variant-method.html?utm_source=rubyflow
* https://prograils.com/posts/rails-5-2-active-storage-new-approach-to-file-uploads

以上。