From 1f2cde6c5eb91292daebc6c6534e2cf5153ece83 Mon Sep 17 00:00:00 2001 From: Doug Felt Date: Tue, 21 Feb 2017 11:39:26 -0800 Subject: [PATCH 1/3] Map to canonical keys when we read in file name mappings. It's a bit cleaner to canonicalize the keys when we read the file names. This means we can just use the one canonical key, instead of using the original to get the file and the canonical one to render text and show the decoding. --- generate_emoji_html.py | 59 ++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/generate_emoji_html.py b/generate_emoji_html.py index 5d3a80b99..594382882 100755 --- a/generate_emoji_html.py +++ b/generate_emoji_html.py @@ -53,30 +53,31 @@ def _merge_keys(dicts): keys.extend(d.keys()) return frozenset(keys) -def _generate_row_cells(key, canonical_key, font, dir_infos, basepaths, colors): + +def _generate_row_cells(key, font, dir_infos, basepaths, colors): CELL_PREFIX = '' indices = range(len(basepaths)) - def _cell(key, info, basepath): + def _cell(info, basepath): if key in info.filemap: - return '' % path.join( - basepath, info.filemap[key]) + return '' % path.join(basepath, info.filemap[key]) return '-missing-' - def _text_cell(key, text_dir): - text = ''.join(unichr(cp) for cp in canonical_key) + + def _text_cell(text_dir): + text = ''.join(unichr(cp) for cp in key) return '%s' % (text_dir, text) if font: row_cells = [ - CELL_PREFIX + _text_cell(key, text_dir) + CELL_PREFIX + _text_cell(text_dir) for text_dir in ('ltr', 'rtl')] else: row_cells = [] row_cells.extend( - [CELL_PREFIX + _cell(key, dir_infos[i], basepaths[i]) + [CELL_PREFIX + _cell(dir_infos[i], basepaths[i]) for i in indices]) if len(colors) > 1: ix = indices[-1] - extension = CELL_PREFIX + _cell(key, dir_infos[ix], basepaths[ix]) + extension = CELL_PREFIX + _cell(dir_infos[ix], basepaths[ix]) row_cells.extend([extension] * (len(colors) - 1)) return row_cells @@ -230,25 +231,16 @@ def _generate_content( lines.append(''.join(header_row)) for key in sorted(all_keys): - row = [] - canonical_key = unicode_data.get_canonical_emoji_sequence(key) - if not canonical_key: - canonical_key = key + row = _generate_row_cells(key, font, dir_infos, basepaths, colors) + row.append(_get_desc(key, dir_infos, basepaths)) + row.append(_get_name(key, annotate)) + lines.append(''.join(row)) - row.extend( - _generate_row_cells( - key, canonical_key, font, dir_infos, basepaths, colors)) - row.append(_get_desc(canonical_key, dir_infos, basepaths)) - row.append(_get_name(canonical_key, annotate)) - try: - lines.append(''.join(row)) - except: - raise Exception('couldn\'t decode %s' % row) return '\n '.join(lines) + '\n' def _get_image_data(image_dir, ext, prefix): - """Return a map from a tuple of cp sequences to a filename. + """Return a map from a canonical tuple of cp sequences to a filename. This filters by file extension, and expects the rest of the files to match the prefix followed by a sequence of hex codepoints separated @@ -267,19 +259,24 @@ def _get_image_data(image_dir, ext, prefix): fails.append('"%s" did not match: "%s"' % (expect_re.pattern, filename)) continue seq = m.group(1) + this_failed = False try: cps = tuple(int(s, 16) for s in seq.split('_')) + for cp in cps: + if (cp > 0x10ffff): + fails.append('cp out of range: ' + filename) + this_failed = True + break + if this_failed: + continue + canonical_cps = unicode_data.get_canonical_emoji_sequence(cps) + if canonical_cps: + # if it is unrecognized, just leave it alone, else replace with + # canonical sequence. + cps = canonical_cps except: fails.append('bad cp sequence: ' + filename) continue - this_failed = False - for cp in cps: - if (cp > 0x10ffff): - fails.append('cp out of range: ' + filename) - this_failed = True - break - if this_failed: - continue if cps in result: fails.append('duplicate sequence: %s and %s' (result[cps], filename)) continue From d24aa6dd3e626d8c9d8146c2edcaa8cd02df0053 Mon Sep 17 00:00:00 2001 From: Doug Felt Date: Tue, 21 Feb 2017 15:18:28 -0800 Subject: [PATCH 2/3] Add all_emoji and emoji_sort options to generate_emoji_html. By default, the list of emoji sequences is based on the union of the sequences encoded in the image file names for all the directories (or the first directory if --limit was set). The --all_emoji option uses the emoji sequences from nototools/unicode_data instead. By default, the list of emoji sequences is in unicode codepoint order. The --emoji_sort option uses the emoji sequence sort order from nototools/unicode_data instead. Along with this, the ordered list of sequences becomes an argument to write_html_page, which it should have been all along. --- generate_emoji_html.py | 69 ++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/generate_emoji_html.py b/generate_emoji_html.py index 594382882..749eb1b1b 100755 --- a/generate_emoji_html.py +++ b/generate_emoji_html.py @@ -135,17 +135,17 @@ def _get_name(key_tuple, annotated_tuples): return CELL_PREFIX + seq_name -def _collect_aux_info(dir_infos, all_keys): +def _collect_aux_info(dir_infos, keys): """Returns a map from dir_info_index to a set of keys of additional images that we will take from the directory at that index.""" target_key_to_info_index = {} - for key in all_keys: + for key in keys: if len(key) == 1: continue for cp in key: target_key = tuple([cp]) - if target_key in all_keys or target_key in target_key_to_info_index: + if target_key in keys or target_key in target_key_to_info_index: continue for i, info in enumerate(dir_infos): if target_key in info.filemap: @@ -164,18 +164,18 @@ def _collect_aux_info(dir_infos, all_keys): def _generate_content( - basedir, font, dir_infos, limit, annotate, standalone, colors): - """Generate an html table for the infos. basedir is the parent directory - of the content, filenames will be made relative to this if underneath it, - else absolute. If limit is true and there are multiple dirs, limit the set of - sequences to those in the first dir. If font is not none, generate columns - for the text rendered in the font before other columns. if annotate is - not none, highlight sequences that appear in this set.""" - - if len(dir_infos) == 1 or limit: - all_keys = frozenset(dir_infos[0].filemap.keys()) - else: - all_keys = _merge_keys([info.filemap for info in dir_infos]) + basedir, font, dir_infos, keys, annotate, standalone, colors): + """Generate an html table for the infos. Basedir is the parent directory of + the content, filenames will be made relative to this if underneath it, else + absolute. If font is not none, generate columns for the text rendered in the + font before other columns. Dir_infos is the list of DirInfos in column + order. Keys is the list of canonical emoji sequences in row order. If + annotate is not none, highlight sequences that appear in this set. If + standalone is true, the image data and font (if used) will be copied under + the basedir to make a completely stand-alone page. Colors is the list of + background colors, the last DirInfo column will be repeated against each of + these backgrounds. + """ basedir = path.abspath(path.expanduser(basedir)) if not path.isdir(basedir): @@ -188,7 +188,7 @@ def _generate_content( # aren't part of main set. e.g. if we have female basketball player # color-3 we want female, basketball player, and color-3 images available # even if they aren't part of the target set. - aux_info = _collect_aux_info(dir_infos, all_keys) + aux_info = _collect_aux_info(dir_infos, keys) # create image subdirectories in target dir, copy image files to them, # and adjust paths @@ -198,8 +198,7 @@ def _generate_content( if not path.isdir(dstdir): os.mkdir(dstdir) - aux_keys = aux_info[i] - copy_keys = all_keys if not aux_keys else (all_keys | aux_keys) + copy_keys = set(keys) | aux_info[i] srcdir = info.directory filemap = info.filemap for key in copy_keys: @@ -230,7 +229,7 @@ def _generate_content( header_row.extend(['Sequence', 'Name']) lines.append(''.join(header_row)) - for key in sorted(all_keys): + for key in keys: row = _generate_row_cells(key, font, dir_infos, basepaths, colors) row.append(_get_desc(key, dir_infos, basepaths)) row.append(_get_name(key, annotate)) @@ -324,6 +323,25 @@ def _get_dir_infos( return infos +def _get_keys(dir_infos, limit, all_emoji, emoji_sort): + """Return a list of the key tuples to display. If all_emoji is + True, returns all emoji sequences, else the sequences available + in dir_infos (limited to the first dir_info if limit is True). + The result is in emoji order if emoji_sort is true, else in + unicode codepoint order.""" + if all_emoji: + keys = unicode_data.get_emoji_sequences() + elif len(dir_infos) == 1 or limit: + keys = frozenset(dir_infos[0].filemap.keys()) + else: + keys = _merge_keys([info.filemap for info in dir_infos]) + if emoji_sort: + sorted_keys = unicode_data.get_sorted_emoji_sequences(keys) + else: + sorted_keys = sorted(keys) + return sorted_keys + + def _parse_annotation_file(afile): annotations = set() line_re = re.compile(r'([0-9a-f ]+)') @@ -386,7 +404,7 @@ STYLE = """ """ def write_html_page( - filename, page_title, font, dir_infos, limit, annotate, standalone, + filename, page_title, font, dir_infos, keys, annotate, standalone, colors): out_dir = path.dirname(filename) @@ -412,7 +430,7 @@ def write_html_page( font = path.normpath(path.join(common_prefix, rel_font)) content = _generate_content( - path.dirname(filename), font, dir_infos, limit, annotate, standalone, + path.dirname(filename), font, dir_infos, keys, annotate, standalone, colors) N_STYLE = STYLE if font: @@ -477,6 +495,10 @@ def main(): parser.add_argument( '-c', '--colors', help='list of colors for background', nargs='*', metavar='hex') + parser.add_argument( + '--all_emoji', help='use all emoji sequences', action='store_true') + parser.add_argument( + '--emoji_sort', help='use emoji sort order', action='store_true') args = parser.parse_args() file_parts = path.splitext(args.outfile) @@ -499,8 +521,11 @@ def main(): args.image_dirs, args.exts, args.prefixes, args.titles, args.default_ext, args.default_prefix) + keys = _get_keys( + dir_infos, args.limit, args.all_emoji, args.emoji_sort) + write_html_page( - args.outfile, args.page_title, args.font, dir_infos, args.limit, + args.outfile, args.page_title, args.font, dir_infos, keys, annotations, args.standalone, args.colors) From df9b5647b7c72d5737c93c7122aa9d370be67b7a Mon Sep 17 00:00:00 2001 From: Doug Felt Date: Wed, 22 Feb 2017 09:58:25 -0800 Subject: [PATCH 3/3] Support 'ok', 'warning', 'error' annotations and define some. Formerly the annotations file created a set of sequences that would cause the name field to display with a special background color. This lets you choose one of three colors by defining the 'type' of annotation in the file. The file format was enhanced and the code using it takes the type of annotation into account. This also adds a sample annotation file with annotations for a number of situations we currently expect to encounter: missing images that we expect to be supported by aliases to other images, flags that we expect to not support, and new unicode 10 emoji that we might not yet have image data for. --- emoji_annotations.txt | 458 +++++++++++++++++++++++++++++++++++++++++ generate_emoji_html.py | 63 ++++-- 2 files changed, 503 insertions(+), 18 deletions(-) create mode 100644 emoji_annotations.txt diff --git a/emoji_annotations.txt b/emoji_annotations.txt new file mode 100644 index 000000000..ab5eb7af0 --- /dev/null +++ b/emoji_annotations.txt @@ -0,0 +1,458 @@ +# annotations +### +### aliases +### +annotation: ok +1f3c3 # RUNNER -> man running +1f3c3 1f3fb # light skin tone +1f3c3 1f3fc # medium-light skin tone +1f3c3 1f3fd # medium skin tone +1f3c3 1f3fe # medium-dark skin tone +1f3c3 1f3ff # dark skin tone +1f3c4 # SURFER -> man surfing +1f3c4 1f3fb # light skin tone +1f3c4 1f3fc # medium-light skin tone +1f3c4 1f3fd # medium skin tone +1f3c4 1f3fe # medium-dark skin tone +1f3c4 1f3ff # dark skin tone +1f3ca # SWIMMER -> man swimming +1f3ca 1f3fb # light skin tone +1f3ca 1f3fc # medium-light skin tone +1f3ca 1f3fd # medium skin tone +1f3ca 1f3fe # medium-dark skin tone +1f3ca 1f3ff # dark skin tone +1f3cb # WEIGHT LIFTER -> man lifting weights +1f3cb 1f3fb # light skin tone +1f3cb 1f3fc # medium-light skin tone +1f3cb 1f3fd # medium skin tone +1f3cb 1f3fe # medium-dark skin tone +1f3cb 1f3ff # dark skin tone +1f3cc # GOLFER -> man golfing +1f3cc 1f3fb # light skin tone +1f3cc 1f3fc # medium-light skin tone +1f3cc 1f3fd # medium skin tone +1f3cc 1f3fe # medium-dark skin tone +1f3cc 1f3ff # dark skin tone +1f46a # FAMILY -> family: man, woman, boy +1f46e # POLICE OFFICER -> man police officer +1f46e 1f3fb # light skin tone +1f46e 1f3fc # medium-light skin tone +1f46e 1f3fd # medium skin tone +1f46e 1f3fe # medium-dark skin tone +1f46e 1f3ff # dark skin tone +1f46f # WOMAN WITH BUNNY EARS -> women with bunny ears partying +1f471 # PERSON WITH BLOND HAIR -> blond-haired man +1f471 1f3fb # light skin tone +1f471 1f3fc # medium-light skin tone +1f471 1f3fd # medium skin tone +1f471 1f3fe # medium-dark skin tone +1f471 1f3ff # dark skin tone +1f473 # MAN WITH TURBAN -> man wearing turban +1f473 1f3fb # light skin tone +1f473 1f3fc # medium-light skin tone +1f473 1f3fd # medium skin tone +1f473 1f3fe # medium-dark skin tone +1f473 1f3ff # dark skin tone +1f477 # CONSTRUCTION WORKER -> man construction worker +1f477 1f3fb # light skin tone +1f477 1f3fc # medium-light skin tone +1f477 1f3fd # medium skin tone +1f477 1f3fe # medium-dark skin tone +1f477 1f3ff # dark skin tone +1f481 # INFORMATION DESK PERSON -> woman tipping hand +1f481 1f3fb # light skin tone +1f481 1f3fc # medium-light skin tone +1f481 1f3fd # medium skin tone +1f481 1f3fe # medium-dark skin tone +1f481 1f3ff # dark skin tone +1f482 # GUARDSMAN -> man guard +1f482 1f3fb # light skin tone +1f482 1f3fc # medium-light skin tone +1f482 1f3fd # medium skin tone +1f482 1f3fe # medium-dark skin tone +1f482 1f3ff # dark skin tone +1f486 # FACE MASSAGE -> woman getting massage +1f486 1f3fb # light skin tone +1f486 1f3fc # medium-light skin tone +1f486 1f3fd # medium skin tone +1f486 1f3fe # medium-dark skin tone +1f486 1f3ff # dark skin tone +1f487 # HAIRCUT -> woman getting haircut +1f487 1f3fb # light skin tone +1f487 1f3fc # medium-light skin tone +1f487 1f3fd # medium skin tone +1f487 1f3fe # medium-dark skin tone +1f487 1f3ff # dark skin tone +1f48f # KISS -> kiss: woman, man +1f491 # COUPLE WITH HEART -> couple with heart: woman, man +1f575 # SLEUTH OR SPY -> man detective +1f575 1f3fb # light skin tone +1f575 1f3fc # medium-light skin tone +1f575 1f3fd # medium skin tone +1f575 1f3fe # medium-dark skin tone +1f575 1f3ff # dark skin tone +1f645 # FACE WITH NO GOOD GESTURE -> woman gesturing NO +1f645 1f3fb # light skin tone +1f645 1f3fc # medium-light skin tone +1f645 1f3fd # medium skin tone +1f645 1f3fe # medium-dark skin tone +1f645 1f3ff # dark skin tone +1f646 # FACE WITH OK GESTURE -> woman gesturing OK +1f646 1f3fb # light skin tone +1f646 1f3fc # medium-light skin tone +1f646 1f3fd # medium skin tone +1f646 1f3fe # medium-dark skin tone +1f646 1f3ff # dark skin tone +1f647 # PERSON BOWING DEEPLY -> man bowing +1f647 1f3fb # light skin tone +1f647 1f3fc # medium-light skin tone +1f647 1f3fd # medium skin tone +1f647 1f3fe # medium-dark skin tone +1f647 1f3ff # dark skin tone +1f64b # HAPPY PERSON RAISING ONE HAND -> woman raising hand +1f64b 1f3fb # light skin tone +1f64b 1f3fc # medium-light skin tone +1f64b 1f3fd # medium skin tone +1f64b 1f3fe # medium-dark skin tone +1f64b 1f3ff # dark skin tone +1f64d # PERSON FROWNING -> woman frowning +1f64d 1f3fb # light skin tone +1f64d 1f3fc # medium-light skin tone +1f64d 1f3fd # medium skin tone +1f64d 1f3fe # medium-dark skin tone +1f64d 1f3ff # dark skin tone +1f64e # PERSON WITH POUTING FACE -> woman pouting +1f64e 1f3fb # light skin tone +1f64e 1f3fc # medium-light skin tone +1f64e 1f3fd # medium skin tone +1f64e 1f3fe # medium-dark skin tone +1f64e 1f3ff # dark skin tone +1f6a3 # ROWBOAT -> man rowing boat +1f6a3 1f3fb # light skin tone +1f6a3 1f3fc # medium-light skin tone +1f6a3 1f3fd # medium skin tone +1f6a3 1f3fe # medium-dark skin tone +1f6a3 1f3ff # dark skin tone +1f6b4 # BICYCLIST -> man biking +1f6b4 1f3fb # light skin tone +1f6b4 1f3fc # medium-light skin tone +1f6b4 1f3fd # medium skin tone +1f6b4 1f3fe # medium-dark skin tone +1f6b4 1f3ff # dark skin tone +1f6b5 # MOUNTAIN BICYCLIST -> man mountain biking +1f6b5 1f3fb # light skin tone +1f6b5 1f3fc # medium-light skin tone +1f6b5 1f3fd # medium skin tone +1f6b5 1f3fe # medium-dark skin tone +1f6b5 1f3ff # dark skin tone +1f6b6 # PEDESTRIAN -> man walking +1f6b6 1f3fb # light skin tone +1f6b6 1f3fc # medium-light skin tone +1f6b6 1f3fd # medium skin tone +1f6b6 1f3fe # medium-dark skin tone +1f6b6 1f3ff # dark skin tone +1f926 # FACE PALM -> woman facepalming +1f926 1f3fb # light skin tone +1f926 1f3fc # medium-light skin tone +1f926 1f3fd # medium skin tone +1f926 1f3fe # medium-dark skin tone +1f926 1f3ff # dark skin tone +1f937 # SHRUG -> woman shrugging +1f937 1f3fb # light skin tone +1f937 1f3fc # medium-light skin tone +1f937 1f3fd # medium skin tone +1f937 1f3fe # medium-dark skin tone +1f937 1f3ff # dark skin tone +1f938 # PERSON DOING CARTWHEEL -> man cartwheeling +1f938 1f3fb # light skin tone +1f938 1f3fc # medium-light skin tone +1f938 1f3fd # medium skin tone +1f938 1f3fe # medium-dark skin tone +1f938 1f3ff # dark skin tone +1f939 # JUGGLING -> man juggling +1f939 1f3fb # light skin tone +1f939 1f3fc # medium-light skin tone +1f939 1f3fd # medium skin tone +1f939 1f3fe # medium-dark skin tone +1f939 1f3ff # dark skin tone +1f93c # WRESTLERS -> men wrestling +1f93d # WATER POLO -> man playing water polo +1f93d 1f3fb # light skin tone +1f93d 1f3fc # medium-light skin tone +1f93d 1f3fd # medium skin tone +1f93d 1f3fe # medium-dark skin tone +1f93d 1f3ff # dark skin tone +1f93e # HANDBALL -> man playing handball +1f93e 1f3fb # light skin tone +1f93e 1f3fc # medium-light skin tone +1f93e 1f3fd # medium skin tone +1f93e 1f3fe # medium-dark skin tone +1f93e 1f3ff # dark skin tone +26f9 # PERSON WITH BALL -> man bouncing ball +26f9 1f3fb # light skin tone +26f9 1f3fc # medium-light skin tone +26f9 1f3fd # medium skin tone +26f9 1f3fe # medium-dark skin tone +26f9 1f3ff # dark skin tone +fe82b # no name -> no name + +# flag aliases +1f1e7 1f1fb # BV -> NO +1f1e8 1f1f5 # CP -> FR +1f1ed 1f1f2 # HM -> AU +1f1f8 1f1ef # SJ -> NO +1f1fa 1f1f2 # UM -> US + +### +### unwanted flags +### +annotation: error +1f1e7 1f1f1 +1f1e7 1f1f6 +1f1e9 1f1ec +1f1ea 1f1e6 +1f1ea 1f1ed +1f1eb 1f1f0 +1f1ec 1f1eb +1f1ec 1f1f5 +1f1ec 1f1f8 +1f1f2 1f1eb +1f1f2 1f1f6 +1f1f3 1f1e8 +1f1f5 1f1f2 +1f1f7 1f1ea +1f1f9 1f1eb +1f1fc 1f1eb +1f1fd 1f1f0 +1f1fe 1f1f9 + +### +### new emoji +### +annotation: warning +1f6f7 +1f6f8 +1f91f +1f91f 1f3fb +1f91f 1f3fc +1f91f 1f3fd +1f91f 1f3fe +1f91f 1f3ff +1f928 +1f929 +1f92a +1f92b +1f92c +1f92d +1f92e +1f92f +1f931 +1f931 1f3fb +1f931 1f3fc +1f931 1f3fd +1f931 1f3fe +1f931 1f3ff +1f932 +1f932 1f3fb +1f932 1f3fc +1f932 1f3fd +1f932 1f3fe +1f932 1f3ff +1f94c +1f961 +1f962 +1f964 +1f965 +1f966 +1f995 +1f996 +1f997 +1f9d0 +1f9d1 +1f9d1 1f3fb +1f9d1 1f3fc +1f9d1 1f3fd +1f9d1 1f3fe +1f9d1 1f3ff +1f9d2 +1f9d2 1f3fb +1f9d2 1f3fc +1f9d2 1f3fd +1f9d2 1f3fe +1f9d2 1f3ff +1f9d3 +1f9d3 1f3fb +1f9d3 1f3fc +1f9d3 1f3fd +1f9d3 1f3fe +1f9d3 1f3ff +1f9d4 +1f9d4 1f3fb +1f9d4 1f3fc +1f9d4 1f3fd +1f9d4 1f3fe +1f9d4 1f3ff +1f9d5 +1f9d5 1f3fb +1f9d5 1f3fc +1f9d5 1f3fd +1f9d5 1f3fe +1f9d5 1f3ff +1f9d6 +1f9d6 1f3fb +1f9d6 1f3fc +1f9d6 1f3fd +1f9d6 1f3fe +1f9d6 1f3ff +1f9d6 200d 2640 +1f9d6 1f3fb 200d 2640 +1f9d6 1f3fc 200d 2640 +1f9d6 1f3fd 200d 2640 +1f9d6 1f3fe 200d 2640 +1f9d6 1f3ff 200d 2640 +1f9d6 200d 2642 +1f9d6 1f3fb 200d 2642 +1f9d6 1f3fc 200d 2642 +1f9d6 1f3fd 200d 2642 +1f9d6 1f3fe 200d 2642 +1f9d6 1f3ff 200d 2642 +1f9d7 +1f9d7 1f3fb +1f9d7 1f3fc +1f9d7 1f3fd +1f9d7 1f3fe +1f9d7 1f3ff +1f9d7 200d 2640 +1f9d7 1f3fb 200d 2640 +1f9d7 1f3fc 200d 2640 +1f9d7 1f3fd 200d 2640 +1f9d7 1f3fe 200d 2640 +1f9d7 1f3ff 200d 2640 +1f9d7 200d 2642 +1f9d7 1f3fb 200d 2642 +1f9d7 1f3fc 200d 2642 +1f9d7 1f3fd 200d 2642 +1f9d7 1f3fe 200d 2642 +1f9d7 1f3ff 200d 2642 +1f9d8 +1f9d8 1f3fb +1f9d8 1f3fc +1f9d8 1f3fd +1f9d8 1f3fe +1f9d8 1f3ff +1f9d8 200d 2640 +1f9d8 1f3fb 200d 2640 +1f9d8 1f3fc 200d 2640 +1f9d8 1f3fd 200d 2640 +1f9d8 1f3fe 200d 2640 +1f9d8 1f3ff 200d 2640 +1f9d8 200d 2642 +1f9d8 1f3fb 200d 2642 +1f9d8 1f3fc 200d 2642 +1f9d8 1f3fd 200d 2642 +1f9d8 1f3fe 200d 2642 +1f9d8 1f3ff 200d 2642 +1f9d9 +1f9d9 1f3fb +1f9d9 1f3fc +1f9d9 1f3fd +1f9d9 1f3fe +1f9d9 1f3ff +1f9d9 200d 2640 +1f9d9 1f3fb 200d 2640 +1f9d9 1f3fc 200d 2640 +1f9d9 1f3fd 200d 2640 +1f9d9 1f3fe 200d 2640 +1f9d9 1f3ff 200d 2640 +1f9d9 200d 2642 +1f9d9 1f3fb 200d 2642 +1f9d9 1f3fc 200d 2642 +1f9d9 1f3fd 200d 2642 +1f9d9 1f3fe 200d 2642 +1f9d9 1f3ff 200d 2642 +1f9da +1f9da 1f3fb +1f9da 1f3fc +1f9da 1f3fd +1f9da 1f3fe +1f9da 1f3ff +1f9da 200d 2640 +1f9da 1f3fb 200d 2640 +1f9da 1f3fc 200d 2640 +1f9da 1f3fd 200d 2640 +1f9da 1f3fe 200d 2640 +1f9da 1f3ff 200d 2640 +1f9da 200d 2642 +1f9da 1f3fb 200d 2642 +1f9da 1f3fc 200d 2642 +1f9da 1f3fd 200d 2642 +1f9da 1f3fe 200d 2642 +1f9da 1f3ff 200d 2642 +1f9db +1f9db 1f3fb +1f9db 1f3fc +1f9db 1f3fd +1f9db 1f3fe +1f9db 1f3ff +1f9db 200d 2640 +1f9db 1f3fb 200d 2640 +1f9db 1f3fc 200d 2640 +1f9db 1f3fd 200d 2640 +1f9db 1f3fe 200d 2640 +1f9db 1f3ff 200d 2640 +1f9db 200d 2642 +1f9db 1f3fb 200d 2642 +1f9db 1f3fc 200d 2642 +1f9db 1f3fd 200d 2642 +1f9db 1f3fe 200d 2642 +1f9db 1f3ff 200d 2642 +1f9dc +1f9dc 1f3fb +1f9dc 1f3fc +1f9dc 1f3fd +1f9dc 1f3fe +1f9dc 1f3ff +1f9dc 200d 2640 +1f9dc 1f3fb 200d 2640 +1f9dc 1f3fc 200d 2640 +1f9dc 1f3fd 200d 2640 +1f9dc 1f3fe 200d 2640 +1f9dc 1f3ff 200d 2640 +1f9dc 200d 2642 +1f9dc 1f3fb 200d 2642 +1f9dc 1f3fc 200d 2642 +1f9dc 1f3fd 200d 2642 +1f9dc 1f3fe 200d 2642 +1f9dc 1f3ff 200d 2642 +1f9dd +1f9dd 1f3fb +1f9dd 1f3fc +1f9dd 1f3fd +1f9dd 1f3fe +1f9dd 1f3ff +1f9dd 200d 2640 +1f9dd 1f3fb 200d 2640 +1f9dd 1f3fc 200d 2640 +1f9dd 1f3fd 200d 2640 +1f9dd 1f3fe 200d 2640 +1f9dd 1f3ff 200d 2640 +1f9dd 200d 2642 +1f9dd 1f3fb 200d 2642 +1f9dd 1f3fc 200d 2642 +1f9dd 1f3fd 200d 2642 +1f9dd 1f3fe 200d 2642 +1f9dd 1f3ff 200d 2642 +1f9de +1f9de 200d 2640 +1f9de 200d 2642 +1f9df +1f9df 200d 2640 +1f9df 200d 2642 +1f9e0 +1f9e1 +1f9e2 +1f9e3 +1f9e4 +1f9e5 +1f9e6 + diff --git a/generate_emoji_html.py b/generate_emoji_html.py index 749eb1b1b..056593738 100755 --- a/generate_emoji_html.py +++ b/generate_emoji_html.py @@ -118,10 +118,10 @@ def _get_desc(key_tuple, dir_infos, basepaths): return CELL_PREFIX + desc -def _get_name(key_tuple, annotated_tuples): +def _get_name(key_tuple, annotations): + annotation = None if annotations is None else annotations.get(key_tuple) CELL_PREFIX = '' % ( - '' if annotated_tuples is None or key_tuple not in annotated_tuples - else ' class="aname"') + '' if annotation is None else ' class="%s"' % annotation) seq_name = unicode_data.get_emoji_sequence_name(key_tuple) if seq_name == None: @@ -164,17 +164,17 @@ def _collect_aux_info(dir_infos, keys): def _generate_content( - basedir, font, dir_infos, keys, annotate, standalone, colors): + basedir, font, dir_infos, keys, annotations, standalone, colors): """Generate an html table for the infos. Basedir is the parent directory of the content, filenames will be made relative to this if underneath it, else absolute. If font is not none, generate columns for the text rendered in the font before other columns. Dir_infos is the list of DirInfos in column order. Keys is the list of canonical emoji sequences in row order. If - annotate is not none, highlight sequences that appear in this set. If - standalone is true, the image data and font (if used) will be copied under - the basedir to make a completely stand-alone page. Colors is the list of - background colors, the last DirInfo column will be repeated against each of - these backgrounds. + annotations is not none, highlight sequences that appear in this map based on + their map values ('ok', 'error', 'warning'). If standalone is true, the + image data and font (if used) will be copied under the basedir to make a + completely stand-alone page. Colors is the list of background colors, the + last DirInfo column will be repeated against each of these backgrounds. """ basedir = path.abspath(path.expanduser(basedir)) @@ -232,7 +232,7 @@ def _generate_content( for key in keys: row = _generate_row_cells(key, font, dir_infos, basepaths, colors) row.append(_get_desc(key, dir_infos, basepaths)) - row.append(_get_name(key, annotate)) + row.append(_get_name(key, annotations)) lines.append(''.join(row)) return '\n '.join(lines) + '\n' @@ -343,17 +343,42 @@ def _get_keys(dir_infos, limit, all_emoji, emoji_sort): def _parse_annotation_file(afile): - annotations = set() - line_re = re.compile(r'([0-9a-f ]+)') + """Parse file and return a map from sequences to one of 'ok', 'warning', + or 'error'. + + The file format consists of two kinds of lines. One defines the annotation + to apply, it consists of the text 'annotation:' followed by one of 'ok', + 'warning', or 'error'. The other defines a sequence that should get the most + recently defined annotation, this is a series of codepoints expressed in hex + separated by spaces. The initial default annotation is 'error'. '#' starts + a comment to end of line, blank lines are ignored. + """ + + annotations = {} + line_re = re.compile(r'annotation:\s*(ok|warning|error)|([0-9a-f ]+)') + annotation = 'error' with open(afile, 'r') as f: for line in f: line = line.strip() if not line or line[0] == '#': continue m = line_re.match(line) - if m: - annotations.add(tuple([int(s, 16) for s in m.group(1).split()])) - return frozenset(annotations) + if not m: + raise Exception('could not parse annotation "%s"' % line) + new_annotation = m.group(1) + if new_annotation: + annotation = new_annotation + else: + seq = tuple([int(s, 16) for s in m.group(2).split()]) + canonical_seq = unicode_data.get_canonical_emoji_sequence(seq) + if canonical_seq: + seq = canonical_seq + if seq in annotations: + raise Exception( + 'duplicate sequence %s in annotations' % + unicode_data.seq_to_string(seq)) + annotations[seq] = annotation + return annotations def _instantiate_template(template, arg_dict): @@ -400,11 +425,13 @@ STYLE = """ vertical-align: bottom; width: 32px; height: 32px } td:last-of-type { background-color: white } - td.aname { background-color: rgb(250, 65, 75) } + td.error { background-color: rgb(250, 65, 75) } + td.warning { background-color: rgb(240, 245, 50) } + td.ok { background-color: rgb(10, 200, 60) } """ def write_html_page( - filename, page_title, font, dir_infos, keys, annotate, standalone, + filename, page_title, font, dir_infos, keys, annotations, standalone, colors): out_dir = path.dirname(filename) @@ -430,7 +457,7 @@ def write_html_page( font = path.normpath(path.join(common_prefix, rel_font)) content = _generate_content( - path.dirname(filename), font, dir_infos, keys, annotate, standalone, + path.dirname(filename), font, dir_infos, keys, annotations, standalone, colors) N_STYLE = STYLE if font: