BigW Consortium Gitlab

gemojione.rake 6.5 KB
Newer Older
1
namespace :gemojione do
2
  desc 'Generates Emoji SHA256 digests'
3
  task digests: ['yarn:check', 'environment'] do
4 5 6
    require 'digest/sha2'
    require 'json'

7 8 9
    # We don't have `node_modules` available in built versions of GitLab
    FileUtils.cp_r(Rails.root.join('node_modules', 'emoji-unicode-version', 'emoji-unicode-version-map.json'), File.join(Rails.root, 'fixtures', 'emojis'))

10
    dir = Gemojione.images_path
11 12
    resultant_emoji_map = {}

Eric Eastwood committed
13
    Gitlab::Emoji.emojis.each do |name, emoji_hash|
14 15 16 17 18 19 20 21 22 23 24
      # Ignore aliases
      unless Gitlab::Emoji.emojis_aliases.key?(name)
        fpath = File.join(dir, "#{emoji_hash['unicode']}.png")
        hash_digest = Digest::SHA256.file(fpath).hexdigest

        entry = {
          category: emoji_hash['category'],
          moji: emoji_hash['moji'],
          unicodeVersion: Gitlab::Emoji.emoji_unicode_version(name),
          digest: hash_digest,
        }
25

26
        resultant_emoji_map[name] = entry
27
      end
28 29 30 31 32
    end

    out = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')

    File.open(out, 'w') do |handle|
33
      handle.write(JSON.pretty_generate(resultant_emoji_map))
34 35 36 37 38 39 40 41 42
    end
  end

  # This task will generate a standard and Retina sprite of all of the current
  # Gemojione Emojis, with the accompanying SCSS map.
  #
  # It will not appear in `rake -T` output, and the dependent gems are not
  # included in the Gemfile by default, because this task will only be needed
  # occasionally, such as when new Emojis are added to Gemojione.
43
  task sprite: :environment do
44 45 46 47 48 49 50
    begin
      require 'sprite_factory'
      require 'rmagick'
    rescue LoadError
      # noop
    end

51 52 53 54 55
    check_requirements!

    SIZE   = 20
    RETINA = SIZE * 2

56 57 58 59 60
    # Update these values to the width and height of the spritesheet when
    # new emoji are added.
    SPRITESHEET_WIDTH = 860
    SPRITESHEET_HEIGHT = 840

61
    # Setup a map to rename image files
Eric Eastwood committed
62 63
    emoji_unicode_string_to_name_map = {}
    Gitlab::Emoji.emojis.each do |name, emoji_hash|
64 65
      # Ignore aliases
      unless Gitlab::Emoji.emojis_aliases.key?(name)
Eric Eastwood committed
66
        emoji_unicode_string_to_name_map[emoji_hash['unicode']] = name
67 68 69 70 71 72 73 74
      end
    end

    # Copy the Gemojione assets to the temporary folder for renaming
    emoji_dir = "app/assets/images/emoji"
    FileUtils.rm_rf(emoji_dir)
    FileUtils.mkdir_p(emoji_dir, mode: 0700)
    FileUtils.cp_r(File.join(Gemojione.images_path, '.'), emoji_dir)
Eric Eastwood committed
75 76 77
    Dir[File.join(emoji_dir, "**/*.png")].each do |png|
      image_path = png
      rename_to_named_emoji_image!(emoji_unicode_string_to_name_map, image_path)
78 79
    end

80
    Dir.mktmpdir do |tmpdir|
81
      FileUtils.cp_r(File.join(emoji_dir, '.'), tmpdir)
82 83 84

      Dir.chdir(tmpdir) do
        Dir["**/*.png"].each do |png|
85 86
          tmp_image_path = File.join(tmpdir, png)
          resize!(tmp_image_path, SIZE)
87 88 89
        end
      end

90
      style_path = Rails.root.join(*%w(app assets stylesheets framework emoji-sprites.scss))
91 92 93

      # Combine the resized assets into a packed sprite and re-generate the SCSS
      SpriteFactory.cssurl = "image-url('$IMAGE')"
94
      SpriteFactory.run!(tmpdir, {
95 96 97 98 99 100 101 102 103 104 105 106 107
        output_style: style_path,
        output_image: "app/assets/images/emoji.png",
        selector:     '.emoji-',
        style:        :scss,
        nocomments:   true,
        pngcrush:     true,
        layout:       :packed
      })

      # SpriteFactory's SCSS is a bit too verbose for our purposes here, so
      # let's simplify it
      system(%Q(sed -i '' "s/width: #{SIZE}px; height: #{SIZE}px; background: image-url('emoji.png')/background-position:/" #{style_path}))
      system(%Q(sed -i '' "s/ no-repeat//" #{style_path}))
108
      system(%Q(sed -i '' "s/ 0px/ 0/g" #{style_path}))
109 110 111 112 113 114 115 116

      # Append a generic rule that applies to all Emojis
      File.open(style_path, 'a') do |f|
        f.puts
        f.puts <<-CSS.strip_heredoc
        .emoji-icon {
          background-image: image-url('emoji.png');
          background-repeat: no-repeat;
117 118
          color: transparent;
          text-indent: -99em;
119 120 121 122 123 124 125 126 127 128
          height: #{SIZE}px;
          width: #{SIZE}px;

          @media only screen and (-webkit-min-device-pixel-ratio: 2),
                 only screen and (min--moz-device-pixel-ratio: 2),
                 only screen and (-o-min-device-pixel-ratio: 2/1),
                 only screen and (min-device-pixel-ratio: 2),
                 only screen and (min-resolution: 192dpi),
                 only screen and (min-resolution: 2dppx) {
            background-image: image-url('emoji@2x.png');
129
            background-size: #{SPRITESHEET_WIDTH}px #{SPRITESHEET_HEIGHT}px;
130 131 132 133 134 135 136 137 138
          }
        }
        CSS
      end
    end

    # Now do it again but for Retina
    Dir.mktmpdir do |tmpdir|
      # Copy the Gemojione assets to the temporary folder for resizing
139
      FileUtils.cp_r(File.join(emoji_dir, '.'), tmpdir)
140 141 142

      Dir.chdir(tmpdir) do
        Dir["**/*.png"].each do |png|
143 144
          tmp_image_path = File.join(tmpdir, png)
          resize!(tmp_image_path, RETINA)
145 146 147 148
        end
      end

      # Combine the resized assets into a packed sprite and re-generate the SCSS
149
      SpriteFactory.run!(tmpdir, {
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
        output_image: "app/assets/images/emoji@2x.png",
        style:        false,
        nocomments:   true,
        pngcrush:     true,
        layout:       :packed
      })
    end
  end

  def check_requirements!
    return if defined?(SpriteFactory) && defined?(Magick)

    puts <<-MSG.strip_heredoc
      This task is disabled by default and should only be run when the Gemojione
      gem is updated with new Emojis.

      To enable this task, *temporarily* add the following lines to Gemfile and
      re-bundle:

      gem 'sprite-factory'
      gem 'rmagick'
    MSG

    exit 1
  end

  def resize!(image_path, size)
    # Resize the image in-place, save it, and free the object
    image = Magick::Image.read(image_path).first
    image.resize!(size, size)
    image.write(image_path) { self.quality = 100 }
    image.destroy!
  end
183 184

  EMOJI_IMAGE_PATH_RE = /(.*?)(([0-9a-f]-?)+)\.png$/i
Eric Eastwood committed
185
  def rename_to_named_emoji_image!(emoji_unicode_string_to_name_map, image_path)
186 187 188 189
    # Rename file from unicode to emoji name
    matches = EMOJI_IMAGE_PATH_RE.match(image_path)
    preceding_path = matches[1]
    unicode_string = matches[2]
Eric Eastwood committed
190
    name = emoji_unicode_string_to_name_map[unicode_string]
191 192 193 194 195
    if name
      new_png_path = File.join(preceding_path, "#{name}.png")
      FileUtils.mv(image_path, new_png_path)
      new_png_path
    else
Eric Eastwood committed
196
      puts "Warning: emoji_unicode_string_to_name_map missing entry for #{unicode_string}. Full path: #{image_path}"
197 198
    end
  end
199
end