225 lines
7.2 KiB
Python
Executable File
225 lines
7.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright 2017 Google Inc. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from __future__ import print_function
|
|
import argparse
|
|
import glob
|
|
import os
|
|
from os import path
|
|
import shutil
|
|
import sys
|
|
|
|
from nototools import unicode_data
|
|
|
|
"""Create aliases in target directory.
|
|
|
|
In addition to links/copies named with aliased sequences, this can also
|
|
create canonically named aliases/copies, if requested."""
|
|
|
|
|
|
DATA_ROOT = path.dirname(path.abspath(__file__))
|
|
|
|
def str_to_seq(seq_str):
|
|
res = [int(s, 16) for s in seq_str.split('_')]
|
|
if 0xfe0f in res:
|
|
print('0xfe0f in file name: %s' % seq_str)
|
|
res = [x for x in res if x != 0xfe0f]
|
|
return tuple(res)
|
|
|
|
|
|
def seq_to_str(seq):
|
|
return '_'.join('%04x' % cp for cp in seq)
|
|
|
|
|
|
def read_default_unknown_flag_aliases():
|
|
unknown_flag_path = path.join(DATA_ROOT, 'unknown_flag_aliases.txt')
|
|
return read_emoji_aliases(unknown_flag_path)
|
|
|
|
|
|
def read_default_emoji_aliases():
|
|
alias_path = path.join(DATA_ROOT, 'emoji_aliases.txt')
|
|
return read_emoji_aliases(alias_path)
|
|
|
|
|
|
def read_emoji_aliases(filename):
|
|
result = {}
|
|
|
|
with open(filename, 'r') as f:
|
|
for line in f:
|
|
ix = line.find('#')
|
|
if (ix > -1):
|
|
line = line[:ix]
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
als, trg = (s.strip() for s in line.split(';'))
|
|
try:
|
|
als_seq = tuple([int(x, 16) for x in als.split('_')])
|
|
trg_seq = tuple([int(x, 16) for x in trg.split('_')])
|
|
except:
|
|
print('cannot process alias %s -> %s' % (als, trg))
|
|
continue
|
|
result[als_seq] = trg_seq
|
|
return result
|
|
|
|
|
|
def add_aliases(
|
|
srcdir, dstdir, aliasfile, prefix, ext, replace=False, copy=False,
|
|
canonical_names=False, dry_run=False):
|
|
"""Use aliasfile to create aliases of files in srcdir matching prefix/ext in
|
|
dstdir. If dstdir is null, use srcdir as dstdir. If replace is false
|
|
and a file already exists in dstdir, report and do nothing. If copy is false
|
|
create a symlink, else create a copy.
|
|
|
|
If canonical_names is true, check all source files and generate aliases/copies
|
|
using the canonical name if different from the existing name.
|
|
|
|
If dry_run is true, report what would be done. Dstdir will be created if
|
|
necessary, even if dry_run is true."""
|
|
|
|
if not path.isdir(srcdir):
|
|
print('%s is not a directory' % srcdir, file=sys.stderr)
|
|
return
|
|
|
|
if not dstdir:
|
|
dstdir = srcdir
|
|
elif not path.isdir(dstdir):
|
|
os.makedirs(dstdir)
|
|
|
|
prefix_len = len(prefix)
|
|
suffix_len = len(ext) + 1
|
|
filenames = [path.basename(f)
|
|
for f in glob.glob(path.join(srcdir, '%s*.%s' % (prefix, ext)))]
|
|
seq_to_file = {
|
|
str_to_seq(name[prefix_len:-suffix_len]) : name
|
|
for name in filenames}
|
|
|
|
aliases = read_emoji_aliases(aliasfile)
|
|
aliases_to_create = {}
|
|
aliases_to_replace = []
|
|
alias_exists = False
|
|
|
|
def check_alias_seq(seq):
|
|
alias_str = seq_to_str(seq)
|
|
alias_name = '%s%s.%s' % (prefix, alias_str, ext)
|
|
alias_path = path.join(dstdir, alias_name)
|
|
if path.exists(alias_path):
|
|
if replace:
|
|
aliases_to_replace.append(alias_name)
|
|
else:
|
|
print('alias %s exists' % alias_str, file=sys.stderr)
|
|
alias_exists = True
|
|
return None
|
|
return alias_name
|
|
|
|
canonical_to_file = {}
|
|
for als, trg in sorted(aliases.items()):
|
|
if trg not in seq_to_file:
|
|
print('target %s for %s does not exist' % (
|
|
seq_to_str(trg), seq_to_str(als)), file=sys.stderr)
|
|
continue
|
|
alias_name = check_alias_seq(als)
|
|
if alias_name:
|
|
target_file = seq_to_file[trg]
|
|
aliases_to_create[alias_name] = target_file
|
|
if canonical_names:
|
|
canonical_seq = unicode_data.get_canonical_emoji_sequence(als)
|
|
if canonical_seq and canonical_seq != als:
|
|
canonical_alias_name = check_alias_seq(canonical_seq)
|
|
if canonical_alias_name:
|
|
canonical_to_file[canonical_alias_name] = target_file
|
|
|
|
if canonical_names:
|
|
print('adding %d canonical aliases' % len(canonical_to_file))
|
|
for seq, f in seq_to_file.iteritems():
|
|
canonical_seq = unicode_data.get_canonical_emoji_sequence(seq)
|
|
if canonical_seq and canonical_seq != seq:
|
|
alias_name = check_alias_seq(canonical_seq)
|
|
if alias_name:
|
|
canonical_to_file[alias_name] = f
|
|
|
|
print('adding %d total canonical sequences' % len(canonical_to_file))
|
|
aliases_to_create.update(canonical_to_file)
|
|
|
|
if replace:
|
|
if not dry_run:
|
|
for k in sorted(aliases_to_replace):
|
|
os.remove(path.join(dstdir, k))
|
|
print('replacing %d files' % len(aliases_to_replace))
|
|
elif alias_exists:
|
|
print('aborting, aliases exist.', file=sys.stderr)
|
|
return
|
|
|
|
for k, v in sorted(aliases_to_create.items()):
|
|
if dry_run:
|
|
msg = 'replace ' if k in aliases_to_replace else ''
|
|
print('%s%s -> %s' % (msg, k, v))
|
|
else:
|
|
try:
|
|
if copy:
|
|
shutil.copy2(path.join(srcdir, v), path.join(dstdir, k))
|
|
else:
|
|
# fix this to create relative symlinks
|
|
if srcdir == dstdir:
|
|
os.symlink(v, path.join(dstdir, k))
|
|
else:
|
|
raise Exception('can\'t create cross-directory symlinks yet')
|
|
except Exception as e:
|
|
print('failed to create %s -> %s' % (k, v), file=sys.stderr)
|
|
raise Exception('oops, ' + str(e))
|
|
print('created %d %s' % (
|
|
len(aliases_to_create), 'copies' if copy else 'symlinks'))
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
'-s', '--srcdir', help='directory containing files to alias',
|
|
required=True, metavar='dir')
|
|
parser.add_argument(
|
|
'-d', '--dstdir', help='directory to write aliases, default srcdir',
|
|
metavar='dir')
|
|
parser.add_argument(
|
|
'-a', '--aliasfile', help='alias file (default emoji_aliases.txt)',
|
|
metavar='file', default='emoji_aliases.txt')
|
|
parser.add_argument(
|
|
'-p', '--prefix', help='file name prefix (default emoji_u)',
|
|
metavar='pfx', default='emoji_u')
|
|
parser.add_argument(
|
|
'-e', '--ext', help='file name extension (default png)',
|
|
choices=['ai', 'png', 'svg'], default='png')
|
|
parser.add_argument(
|
|
'-r', '--replace', help='replace existing files/aliases',
|
|
action='store_true')
|
|
parser.add_argument(
|
|
'-c', '--copy', help='create a copy of the file, not a symlink',
|
|
action='store_true')
|
|
parser.add_argument(
|
|
'--canonical_names', help='include extra copies with canonical names '
|
|
'(including fe0f emoji presentation character)', action='store_true');
|
|
parser.add_argument(
|
|
'-n', '--dry_run', help='print out aliases to create only',
|
|
action='store_true')
|
|
args = parser.parse_args()
|
|
|
|
add_aliases(
|
|
args.srcdir, args.dstdir, args.aliasfile, args.prefix, args.ext,
|
|
args.replace, args.copy, args.canonical_names, args.dry_run)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|