🔥 obsolete scripts
This commit is contained in:
@@ -16,9 +16,6 @@ gem 'gemoji'
|
||||
|
||||
### Extract images
|
||||
|
||||
To obtain image files as fallbacks for browsers and OS's that don't support
|
||||
emoji, run the `gemoji extract` command **on macOS Sierra or later**:
|
||||
|
||||
``` sh
|
||||
bundle exec gemoji extract public/images/emoji
|
||||
```
|
||||
@@ -26,7 +23,6 @@ bundle exec gemoji extract public/images/emoji
|
||||
This will extract images into filenames such as:
|
||||
|
||||
* `public/images/emoji/octocat.png`
|
||||
* `public/images/emoji/unicode/1f9c0.png` (the `:cheese:` emoji)
|
||||
|
||||
|
||||
Example Rails Helper
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
set jsCode to "document.getElementById('output').value"
|
||||
set json to missing value
|
||||
|
||||
tell application "Safari"
|
||||
repeat
|
||||
set json to (do JavaScript jsCode in current tab of window 1)
|
||||
if (json is not missing value) then exit repeat
|
||||
delay 0.5
|
||||
end repeat
|
||||
close current tab of window 1
|
||||
end tell
|
||||
|
||||
return json
|
||||
@@ -1,86 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<title>Emoji alias detection</title>
|
||||
<style>
|
||||
textarea {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Save the following as <tt>emoji.json</tt>:</p>
|
||||
|
||||
<textarea id="output" rows="50" cols="80"></textarea>
|
||||
|
||||
<script>
|
||||
const VARIATION_SELECTOR_16 = /\ufe0f$/
|
||||
|
||||
function detectAliases(db) {
|
||||
const canvas = document.createElement('canvas')
|
||||
|
||||
for (const emoji of db) {
|
||||
const raw = emoji.emoji
|
||||
if (!raw) continue
|
||||
|
||||
const candidates = [raw]
|
||||
if (VARIATION_SELECTOR_16.test(raw)) {
|
||||
candidates.unshift(raw.replace(VARIATION_SELECTOR_16, ''))
|
||||
} else {
|
||||
candidates.push(`${raw}\u{fe0f}`)
|
||||
}
|
||||
|
||||
const newRaw = candidates.filter(e => detectEmoji(canvas, e))[0]
|
||||
|
||||
if (newRaw && newRaw != raw) {
|
||||
emoji.emoji = newRaw
|
||||
console.info('new raw representation found for %s :%s:', raw, emoji.aliases[0])
|
||||
}
|
||||
if (!newRaw) {
|
||||
console.warn('no raw representation for %s :%s:', raw, emoji.aliases[0])
|
||||
}
|
||||
}
|
||||
|
||||
dump(db)
|
||||
}
|
||||
|
||||
function detectEmoji(canvas, emoji) {
|
||||
const context = canvas.getContext('2d')
|
||||
drawEmoji(context, emoji)
|
||||
|
||||
let supported = false
|
||||
const data = context.getImageData(0, 16, 64, 1).data
|
||||
|
||||
for (let x = 0; x <= 64; x++) {
|
||||
if (x <= 32) {
|
||||
// character is supported if there are any colored pixels found
|
||||
if (data[4*x] || data[4*x + 1] || data[4*x + 2]) supported = true
|
||||
} else if (x >= 48 && data[4*x + 3] > 0) {
|
||||
// however, if more than one character got rendered, treat as unsupported
|
||||
supported = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return supported
|
||||
}
|
||||
|
||||
function drawEmoji(context, emoji) {
|
||||
context.clearRect(0, 0, 400, 400)
|
||||
context.fillStyle = '#000'
|
||||
context.textBaseline = 'top'
|
||||
context.font = '32px sans-serif, "Apple Color Emoji", "Segoe UI", "Segoe UI Emoji", "Segoe UI Symbol"'
|
||||
context.fillText(emoji+'___', 0, 0)
|
||||
}
|
||||
|
||||
function dump(db) {
|
||||
var json = JSON.stringify(db, null, ' ')
|
||||
.replace(/^( +)(.+)\[\](,?)$/mg, "$1$2[\n$1]$3")
|
||||
.replace(/,\n( *) /g, "\n$1, ")
|
||||
document.getElementById('output').value = `${json}\n`
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest
|
||||
xhr.onload = function() {
|
||||
detectAliases(JSON.parse(xhr.responseText))
|
||||
};
|
||||
xhr.open('GET', 'emoji.json', false)
|
||||
xhr.send(null)
|
||||
</script>
|
||||
@@ -1,6 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'emoji/extractor'
|
||||
require 'fileutils'
|
||||
require 'optparse'
|
||||
|
||||
@@ -37,25 +36,14 @@ module Emoji
|
||||
puts usage_text
|
||||
end
|
||||
|
||||
VALID_SIZES = [ 20, 32, 40, 48, 64, 96, 160 ]
|
||||
|
||||
def extract(argv)
|
||||
size = 64
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.on("--size=#{size}", Integer) do |val|
|
||||
if VALID_SIZES.include?(val)
|
||||
size = val
|
||||
else
|
||||
raise InvalidUsage, "size should be one of: #{VALID_SIZES.join(', ')}"
|
||||
end
|
||||
end
|
||||
end.parse!(argv)
|
||||
# OptionParser.new do |opts|
|
||||
# opts.on("--size=64", Integer) do |size|
|
||||
# end.parse!(argv)
|
||||
|
||||
raise InvalidUsage unless argv.size == 1
|
||||
path = argv[0]
|
||||
|
||||
Emoji::Extractor.new(size, path).extract!
|
||||
Dir["#{Emoji.images_path}/*.png"].each do |png|
|
||||
FileUtils.cp(png, File.join(path, File.basename(png)))
|
||||
end
|
||||
@@ -63,7 +51,7 @@ module Emoji
|
||||
|
||||
def usage_text
|
||||
<<EOF
|
||||
Usage: gemoji extract <path> [--size=64]
|
||||
Usage: gemoji extract <path>
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'emoji'
|
||||
require 'fileutils'
|
||||
|
||||
module Emoji
|
||||
class Extractor
|
||||
EMOJI_TTF = "/System/Library/Fonts/Apple Color Emoji.ttc"
|
||||
|
||||
attr_reader :size, :images_path
|
||||
|
||||
def initialize(size, images_path)
|
||||
@size = size
|
||||
@images_path = images_path
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
return to_enum(__method__) unless block_given?
|
||||
|
||||
File.open(EMOJI_TTF, 'rb') do |file|
|
||||
font_offsets = parse_ttc(file)
|
||||
file.pos = font_offsets[0]
|
||||
|
||||
tables = parse_tables(file)
|
||||
glyph_index = extract_glyph_index(file, tables)
|
||||
|
||||
each_glyph_bitmap(file, tables, glyph_index, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def extract!
|
||||
each do |glyph_name, type, binread|
|
||||
if emoji = glyph_name_to_emoji(glyph_name)
|
||||
image_filename = "#{images_path}/#{emoji.image_filename}"
|
||||
FileUtils.mkdir_p(File.dirname(image_filename))
|
||||
File.open(image_filename, 'wb') { |f| f.write binread.call }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
GENDER_MAP = {
|
||||
"M" => "\u{2642}",
|
||||
"W" => "\u{2640}",
|
||||
}
|
||||
|
||||
FAMILY_MAP = {
|
||||
"B" => "\u{1f466}",
|
||||
"G" => "\u{1f467}",
|
||||
"M" => "\u{1f468}",
|
||||
"W" => "\u{1f469}",
|
||||
}.freeze
|
||||
|
||||
FAMILY = "1F46A"
|
||||
COUPLE = "1F491"
|
||||
KISS = "1F48F"
|
||||
|
||||
def glyph_name_to_emoji(glyph_name)
|
||||
return if glyph_name =~ /\.[1-5]($|\.)/
|
||||
zwj = Emoji::ZERO_WIDTH_JOINER
|
||||
v16 = Emoji::VARIATION_SELECTOR_16
|
||||
|
||||
if glyph_name =~ /^u(#{FAMILY}|#{COUPLE}|#{KISS})\.([#{FAMILY_MAP.keys.join('')}]+)$/
|
||||
if $1 == FAMILY ? $2 == "MWB" : $2 == "WM"
|
||||
raw = [$1.hex].pack('U')
|
||||
else
|
||||
if $1 == COUPLE
|
||||
middle = "#{zwj}\u{2764}#{v16}#{zwj}" # heavy black heart
|
||||
elsif $1 == KISS
|
||||
middle = "#{zwj}\u{2764}#{v16}#{zwj}\u{1F48B}#{zwj}" # heart + kiss mark
|
||||
else
|
||||
middle = zwj
|
||||
end
|
||||
raw = $2.split('').map { |c| FAMILY_MAP.fetch(c) }.join(middle)
|
||||
end
|
||||
candidates = [raw]
|
||||
else
|
||||
raw = glyph_name.gsub(/(^|_)u([0-9A-F]+)/) { ($1.empty?? $1 : zwj) + [$2.hex].pack('U') }
|
||||
raw.sub!(/\.0\b/, '')
|
||||
raw.sub!(/\.(#{GENDER_MAP.keys.join('|')})$/) { v16 + zwj + GENDER_MAP.fetch($1) }
|
||||
candidates = [raw]
|
||||
candidates << raw.sub(v16, '') if raw.include?(v16)
|
||||
candidates << raw.gsub(zwj, '') if raw.include?(zwj)
|
||||
candidates.dup.each { |c| candidates << (c + v16) }
|
||||
end
|
||||
|
||||
candidates.map { |c| Emoji.find_by_unicode(c) }.compact.first
|
||||
end
|
||||
|
||||
# https://www.microsoft.com/typography/otspec/otff.htm
|
||||
def parse_ttc(io)
|
||||
header_name = io.read(4).unpack('a*')[0]
|
||||
raise unless "ttcf" == header_name
|
||||
header_version, num_fonts = io.read(4*2).unpack('l>N')
|
||||
# parse_version(header_version) #=> 2.0
|
||||
io.read(4 * num_fonts).unpack('N*')
|
||||
end
|
||||
|
||||
def parse_tables(io)
|
||||
sfnt_version, num_tables = io.read(4 + 2*4).unpack('Nn')
|
||||
# sfnt_version #=> 0x00010000
|
||||
num_tables.times.each_with_object({}) do |_, tables|
|
||||
tag, checksum, offset, length = io.read(4 + 4*3).unpack('a4N*')
|
||||
tables[tag] = {
|
||||
checksum: checksum,
|
||||
offset: offset,
|
||||
length: length,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
GlyphIndex = Struct.new(:length, :name_index, :names) do
|
||||
def name_for(glyph_id)
|
||||
index = name_index[glyph_id]
|
||||
names[index - 257]
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
length.times(&block)
|
||||
end
|
||||
|
||||
def each_with_name
|
||||
each do |glyph_id|
|
||||
yield glyph_id, name_for(glyph_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def extract_glyph_index(io, tables)
|
||||
postscript_table = tables.fetch('post')
|
||||
io.pos = postscript_table[:offset]
|
||||
end_pos = io.pos + postscript_table[:length]
|
||||
|
||||
parse_version(io.read(32).unpack('l>')[0]) #=> 2.0
|
||||
num_glyphs = io.read(2).unpack('n')[0]
|
||||
glyph_name_index = io.read(2*num_glyphs).unpack('n*')
|
||||
|
||||
glyph_names = []
|
||||
while io.pos < end_pos
|
||||
length = io.read(1).unpack('C')[0]
|
||||
glyph_names << io.read(length)
|
||||
end
|
||||
|
||||
GlyphIndex.new(num_glyphs, glyph_name_index, glyph_names)
|
||||
end
|
||||
|
||||
# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html
|
||||
def each_glyph_bitmap(io, tables, glyph_index)
|
||||
io.pos = sbix_offset = tables.fetch('sbix')[:offset]
|
||||
strike = extract_sbix_strike(io, glyph_index.length, size)
|
||||
|
||||
glyph_index.each_with_name do |glyph_id, glyph_name|
|
||||
glyph_offset = strike[:glyph_data_offset][glyph_id]
|
||||
next_glyph_offset = strike[:glyph_data_offset][glyph_id + 1]
|
||||
|
||||
if glyph_offset && next_glyph_offset && glyph_offset < next_glyph_offset
|
||||
io.pos = sbix_offset + strike[:offset] + glyph_offset
|
||||
x, y, type = io.read(2*2 + 4).unpack('s2A4')
|
||||
yield glyph_name, type, -> { io.read(next_glyph_offset - glyph_offset - 8) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def extract_sbix_strike(io, num_glyphs, image_size)
|
||||
sbix_offset = io.pos
|
||||
version, flags, num_strikes = io.read(2*2 + 4).unpack('n2N')
|
||||
strike_offsets = num_strikes.times.map { io.read(4).unpack('N')[0] }
|
||||
|
||||
strike_offsets.each do |strike_offset|
|
||||
io.pos = sbix_offset + strike_offset
|
||||
ppem, resolution = io.read(4*2).unpack('n2')
|
||||
next unless ppem == size
|
||||
|
||||
data_offsets = io.read(4 * (num_glyphs+1)).unpack('N*')
|
||||
return {
|
||||
ppem: ppem,
|
||||
resolution: resolution,
|
||||
offset: strike_offset,
|
||||
glyph_data_offset: data_offsets,
|
||||
}
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
def parse_version(num)
|
||||
major = num >> 16
|
||||
minor = num & 0xFFFF
|
||||
"#{major}.#{minor}"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Usage: script/regenerate
|
||||
#
|
||||
# Note: only usable on OS X
|
||||
#
|
||||
# Combines `rake db:dump` and `db/aliases.html` filter to regenerate the
|
||||
# `db/emoji.json` file using only emoji that are guaranteed to not render as
|
||||
# ordinary characters on OS X.
|
||||
|
||||
set -e
|
||||
|
||||
case "$1" in
|
||||
-h | --help )
|
||||
sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$(uname -s)" != "Darwin" ]; then
|
||||
echo "Error: this script must be run under Mac OS X." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bundle exec rake db:dump > db/emoji2.json
|
||||
mv db/emoji2.json db/emoji.json
|
||||
|
||||
open -g -a Safari db/aliases.html
|
||||
osascript db/aliases.applescript | sed '/^$/d' > db/emoji2.json
|
||||
mv db/emoji2.json db/emoji.json
|
||||
|
||||
if git diff --exit-code --stat -- db/emoji.json; then
|
||||
echo "Done. The file \`db/emoji.json\` remains unchanged."
|
||||
fi
|
||||
Reference in New Issue
Block a user