BigW Consortium Gitlab

gemojione.rake 6.55 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
      # 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'],
22
          description: emoji_hash['description'],
23
          unicodeVersion: Gitlab::Emoji.emoji_unicode_version(name),
24
          digest: hash_digest
25
        }
26

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

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

    File.open(out, 'w') do |handle|
34
      handle.write(JSON.pretty_generate(resultant_emoji_map))
35 36 37 38 39 40 41 42 43
    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.
44
  task sprite: :environment do
45 46 47 48 49 50 51
    begin
      require 'sprite_factory'
      require 'rmagick'
    rescue LoadError
      # noop
    end

52 53 54 55 56
    check_requirements!

    SIZE   = 20
    RETINA = SIZE * 2

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

62
    # Setup a map to rename image files
Eric Eastwood committed
63 64
    emoji_unicode_string_to_name_map = {}
    Gitlab::Emoji.emojis.each do |name, emoji_hash|
65 66
      # Ignore aliases
      unless Gitlab::Emoji.emojis_aliases.key?(name)
Eric Eastwood committed
67
        emoji_unicode_string_to_name_map[emoji_hash['unicode']] = name
68 69 70 71 72 73 74 75
      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
76 77 78
    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)
79 80
    end

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

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

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

      # Combine the resized assets into a packed sprite and re-generate the SCSS
      SpriteFactory.cssurl = "image-url('$IMAGE')"
95
      SpriteFactory.run!(tmpdir, {
96 97 98 99 100 101 102 103 104 105 106 107 108
        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}))
109
      system(%Q(sed -i '' "s/ 0px/ 0/g" #{style_path}))
110 111 112 113 114 115 116 117

      # 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;
118 119
          color: transparent;
          text-indent: -99em;
120 121 122 123 124 125 126 127 128 129
          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');
130
            background-size: #{SPRITESHEET_WIDTH}px #{SPRITESHEET_HEIGHT}px;
131 132 133 134 135 136 137 138 139
          }
        }
        CSS
      end
    end

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

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

      # Combine the resized assets into a packed sprite and re-generate the SCSS
150
      SpriteFactory.run!(tmpdir, {
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 183
        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
184 185

  EMOJI_IMAGE_PATH_RE = /(.*?)(([0-9a-f]-?)+)\.png$/i
Eric Eastwood committed
186
  def rename_to_named_emoji_image!(emoji_unicode_string_to_name_map, image_path)
187 188 189 190
    # 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
191
    name = emoji_unicode_string_to_name_map[unicode_string]
192 193 194 195 196
    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
197
      puts "Warning: emoji_unicode_string_to_name_map missing entry for #{unicode_string}. Full path: #{image_path}"
198 199
    end
  end
200
end