1# -*- coding: utf-8 -*-
2# Copyright 2010-2018, Google Inc.
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met:
8#
9#     * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11#     * Redistributions in binary form must reproduce the above
12# copyright notice, this list of conditions and the following disclaimer
13# in the documentation and/or other materials provided with the
14# distribution.
15#     * Neither the name of Google Inc. nor the names of its
16# contributors may be used to endorse or promote products derived from
17# this software without specific prior written permission.
18#
19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31"""Generates svg files from templates.
32
33Usage:
34$ mkdir OUTPUT_PATH
35$ python transform.py --input_dir=data/images/android/template --output_dir=OUTPUT_PATH
36
37Note:
38We use half-width characters for following to adjust the position
39on software keyboards.
40See also: b/16722519
41
42"(", ")", "!", "?", ":"
43
44Half-width is also used for "/" for better look.
45"""
46
47import logging
48import optparse
49import os
50import tempfile
51import zipfile
52
53
54_REPLACE_CHAR = '中'
55_REPLACE_CHAR_QWERTY_SUPPORT = '副'
56
57_REPLACE_CHARS = ('中', '左', '上', '右', '下')
58
59_REPLACE_ID_SUFFIXEX = (
60    '{id_suffix_center}', '{id_suffix_left}', '{id_suffix_up}',
61    '{id_suffix_right}', '{id_suffix_down}')
62
63_EXPAND_SUPPORT_SUFFIXES = (
64    'center', 'left', 'up', 'right', 'down', 'release')
65
66_GODAN_KANA_SUPPORT_KEY_DEFINITIONS = [
67    {'name': '01',
68     'chars': ('A', '', '', '', '1'),
69     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
70    {'name': '02',
71     'chars': ('K', '', 'Q', 'G', '2'),
72     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
73    {'name': '03',
74     'chars': ('H', 'P', 'F', 'B', '3'),
75     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
76    {'name': '04',
77     'chars': ('I', '', '', '', '4'),
78     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
79    {'name': '05',
80     'chars': ('S', '', 'J', 'Z', '5'),
81     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
82    {'name': '06',
83     'chars': ('M', '/', 'L', 'ー', '6'),
84     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
85    {'name': '07',
86     'chars': ('U', '', '', '', '7'),
87     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
88    {'name': '08',
89     'chars': ('T', '', 'C', 'D', '8'),
90     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
91    {'name': '09',
92     'chars': ('Y', '(', 'X', ')', '9'),
93     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
94    {'name': '10',
95     'chars': ('E', '', '', '', ''),
96     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
97    {'name': '11',
98     'chars': ('N', ':', '', '・', '0'),
99     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
100    # 12: Needs another template. Defined bellow
101    {'name': '13',
102     'chars': ('O', '', '', '', ''),
103     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
104    # 14: "大小" key(godan__kana__14.svg): Will be modified directly
105    {'name': '15',
106     'chars': ('W', '「', 'V', '」', ''),
107     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
108    ]
109
110_GODAN_KANA_SUPPORT_12_KEY_DEFINITION = [
111    {'name': '12',
112     'chars': ('R', '。', '?', '!', '、'),
113     'id_suffixes': ('', '-light', '-light', '-light', '-light')},
114    ]
115
116_GODAN_KANA_SUPPORT_POPUP_KEY_DEFINITIONS = [
117    {'name': '01', 'chars': ('A', '', '', '', '1')},
118    {'name': '02', 'chars': ('K', '', 'Q', 'G', '2')},
119    {'name': '03', 'chars': ('H', 'P', 'F', 'B', '3')},
120    {'name': '04', 'chars': ('I', '', '', '', '4')},
121    {'name': '05', 'chars': ('S', '', 'J', 'Z', '5')},
122    {'name': '06', 'chars': ('M', '/', 'L', 'ー', '6')},
123    {'name': '07', 'chars': ('U', '', '', '', '7')},
124    {'name': '08', 'chars': ('T', '', 'C', 'D', '8')},
125    {'name': '09', 'chars': ('Y', '(', 'X', ')', '9')},
126    {'name': '10', 'chars': ('E', '', '', '', '')},
127    # Use half-width ':' to adjust character position.
128    {'name': '11', 'chars': ('N', ':', 'ん', '・', '0')},
129    {'name': '12', 'chars': ('R', '。', '?', '!', '、')},
130    {'name': '13', 'chars': ('O', '', '', '', '')},
131    # 14: "大小" key: godan__kana__14.svg will be used.
132    # Please check mozc/android/resources/res/xml/
133    {'name': '15', 'chars': ('W', '「', 'V', '」', '')},
134    ]
135
136_QWERTY_SUPPORT_KEY_MAPPING = {
137    'q': '1',
138    'w': '2',
139    'e': '3',
140    'r': '4',
141    't': '5',
142    'y': '6',
143    'u': '7',
144    'i': '8',
145    'o': '9',
146    'p': '0',
147}
148
149# 'A'-'Z' will be generated by GenerateQwertyAlphabetKeyDefinitions().
150_QWERTY_KEYICON_KEY_DEFINITIONS = [
151    {'name': 'digit_zero', 'char': '0'},
152    {'name': 'digit_one', 'char': '1'},
153    {'name': 'digit_two', 'char': '2'},
154    {'name': 'digit_three', 'char': '3'},
155    {'name': 'digit_four', 'char': '4'},
156    {'name': 'digit_five', 'char': '5'},
157    {'name': 'digit_six', 'char': '6'},
158    {'name': 'digit_seven', 'char': '7'},
159    {'name': 'digit_eight', 'char': '8'},
160    {'name': 'digit_nine', 'char': '9'},
161
162    {'name': 'ampersand', 'char': '&'},
163    {'name': 'apostrophe', 'char': '''},
164    {'name': 'asterisk', 'char': '*'},
165    {'name': 'circumflex_accent', 'char': '^'},
166    {'name': 'colon', 'char': ':'},
167    {'name': 'comma', 'char': ','},
168    {'name': 'commercial_at', 'char': '@'},
169    {'name': 'dollar_sign', 'char': '$'},
170    {'name': 'equals_sign', 'char': '='},
171    {'name': 'exclamation_mark', 'char': '!'},
172    {'name': 'full_stop', 'char': '.'},
173    {'name': 'grave_accent', 'char': '`'},
174    {'name': 'less_than_sign', 'char': '<'},
175    {'name': 'greater_than_sign', 'char': '>'},
176    {'name': 'hyphen_minus', 'char': '-'},
177    {'name': 'ideographic_comma', 'char': '、'},
178    {'name': 'ideographic_full_stop', 'char': '。'},
179    {'name': 'katakana_middle_dot', 'char': '・'},
180    {'name': 'left_corner_bracket', 'char': '「'},
181    {'name': 'right_corner_bracket', 'char': '」'},
182    {'name': 'left_curly_bracket', 'char': '{'},
183    {'name': 'right_curly_bracket', 'char': '}'},
184    {'name': 'left_parenthesis', 'char': '('},
185    {'name': 'right_parenthesis', 'char': ')'},
186    {'name': 'left_square_bracket', 'char': '['},
187    {'name': 'right_square_bracket', 'char': ']'},
188    {'name': 'low_line', 'char': '_'},
189    {'name': 'minus_sign', 'char': '−'},
190    {'name': 'number_sign', 'char': '#'},
191    {'name': 'percent_sign', 'char': '%'},
192    {'name': 'plus_sign', 'char': '+'},
193    {'name': 'question_mark', 'char': '?'},
194    {'name': 'quotation_mark', 'char': '"'},
195    {'name': 'reverse_solidus', 'char': '\\'},
196    {'name': 'semicolon', 'char': ';'},
197    {'name': 'solidus', 'char': '/'},
198    {'name': 'tilde', 'char': '~'},
199    {'name': 'vertical_line', 'char': '|'},
200
201    # Use half-width ':' to adjust character position.
202    {'name': 'fullwidth_colon', 'char': ':'},
203
204    # Use half-width '/' for a better look.
205    # TODO(team): This rule generates qwerty__keyicon__fullwidth__solidus but
206    # currently it's used only by GODAN.  Rename it.
207    {'name': 'fullwidth_solidus', 'char': '/'},
208    ]
209
210_TWELVEKEYS_ALPHABET_KEY_DEFINITIONS = [
211    # 01, 11 and 12 have spaces among their characters.
212    # Without them shown characters are too close.
213    {'name': '01', 'char': '@ - _ /'},
214    {'name': '02', 'char': 'ABC'},
215    {'name': '03', 'char': 'DEF'},
216    {'name': '04', 'char': 'GHI'},
217    {'name': '05', 'char': 'JKL'},
218    {'name': '06', 'char': 'MNO'},
219    {'name': '07', 'char': 'PQRS'},
220    {'name': '08', 'char': 'TUV'},
221    {'name': '09', 'char': 'WXYZ'},
222    # 10: "a-A" key. Will be modified directly.
223    {'name': '11', 'char': '\' " : ;'},
224    {'name': '12', 'char': '. , ? !'},
225    ]
226
227_TWELVEKEYS_ALPHABET_SUPPORT_KEY_DEFINITIONS = [
228    # 01, 11 and 12 have spaces among their characters.
229    # Without them shown characters are too close.
230    {'name': '01', 'chars': ('@ - _ /', '', '', '', '1')},
231    {'name': '02', 'chars': ('ABC', '', '', '', '2')},
232    {'name': '03', 'chars': ('DEF', '', '', '', '3')},
233    {'name': '04', 'chars': ('GHI', '', '', '', '4')},
234    {'name': '05', 'chars': ('JKL', '', '', '', '5')},
235    {'name': '06', 'chars': ('MNO', '', '', '', '6')},
236    {'name': '07', 'chars': ('PQRS', '', '', '', '7')},
237    {'name': '08', 'chars': ('TUV', '', '', '', '8')},
238    {'name': '09', 'chars': ('WXYZ', '', '', '', '9')},
239    # 10: "a-A" key. Will be modified directly.
240    {'name': '11', 'chars': ('\' " : ;', '', '', '', '0')},
241    {'name': '12', 'chars': ('. , ? !', '', '', '', '')},
242    ]
243
244_TWELVEKEYS_ALPHABET_SUPPORT_POPUP_KEY_DEFINITIONS = [
245    {'name': '01', 'chars': ('@', '-', '_', '/', '1')},
246    {'name': '02', 'chars': ('A', 'B', 'C', '', '2')},
247    {'name': '03', 'chars': ('D', 'E', 'F', '', '3')},
248    {'name': '04', 'chars': ('G', 'H', 'I', '', '4')},
249    {'name': '05', 'chars': ('J', 'K', 'L', '', '5')},
250    {'name': '06', 'chars': ('M', 'N', 'O', '', '6')},
251    {'name': '07', 'chars': ('P', 'Q', 'R', 'S', '7')},
252    {'name': '08', 'chars': ('T', 'U', 'V', '', '8')},
253    {'name': '09', 'chars': ('W', 'X', 'Y', 'Z', '9')},
254    # 10: "a-A" key: twelvekeys__alphabet__10.svg will be used.
255    # Please check mozc/android/resources/res/xml/
256    {'name': '11', 'chars': (''', '"', ':', ';', '0')},
257    {'name': '12', 'chars': ('.', ',', '?', '!', '')},
258    ]
259
260_TWELVEKEYS_KANA_KEYICON_KEY_DEFINITIONS = [
261    {'name': 'exclamation_mark', 'char': '!'},
262    {'name': 'ideographic_period', 'char': '。'},
263    {'name': 'left_parenthesis', 'char': '('},
264    {'name': 'prolonged_sound_mark', 'char': 'ー'},
265    {'name': 'question_mark', 'char': '?'},
266    {'name': 'right_parenthesis', 'char': ')'},
267    {'name': 'horizontal_ellipsis', 'char': '…'},
268    {'name': 'wave_dash', 'char': '〜'},
269
270    {'name': 'a', 'char': 'あ'},
271    {'name': 'i', 'char': 'い'},
272    {'name': 'u', 'char': 'う'},
273    {'name': 'e', 'char': 'え'},
274    {'name': 'o', 'char': 'お'},
275    {'name': 'an', 'char': 'あん'},
276    {'name': 'in', 'char': 'いん'},
277    {'name': 'un', 'char': 'うん'},
278    {'name': 'en', 'char': 'えん'},
279    {'name': 'on', 'char': 'おん'},
280    {'name': 'axtu', 'char': 'あっ'},
281    {'name': 'ixtu', 'char': 'いっ'},
282    {'name': 'uxtu', 'char': 'うっ'},
283    {'name': 'extu', 'char': 'えっ'},
284    {'name': 'oxtu', 'char': 'おっ'},
285    {'name': 'xi', 'char': 'ぃ'},
286    {'name': 'xe', 'char': 'ぇ'},
287
288    {'name': 'ka', 'char': 'か'},
289    {'name': 'ki', 'char': 'き'},
290    {'name': 'ku', 'char': 'く'},
291    {'name': 'ke', 'char': 'け'},
292    {'name': 'ko', 'char': 'こ'},
293
294    {'name': 'sa', 'char': 'さ'},
295    {'name': 'shi', 'char': 'し'},
296    {'name': 'su', 'char': 'す'},
297    {'name': 'se', 'char': 'せ'},
298    {'name': 'so', 'char': 'そ'},
299
300    {'name': 'ta', 'char': 'た'},
301    {'name': 'chi', 'char': 'ち'},
302    {'name': 'tsu', 'char': 'つ'},
303    {'name': 'te', 'char': 'て'},
304    {'name': 'to', 'char': 'と'},
305
306    {'name': 'na', 'char': 'な'},
307    {'name': 'ni', 'char': 'に'},
308    {'name': 'nu', 'char': 'ぬ'},
309    {'name': 'ne', 'char': 'ね'},
310    {'name': 'no', 'char': 'の'},
311
312    {'name': 'ha', 'char': 'は'},
313    {'name': 'hi', 'char': 'ひ'},
314    {'name': 'fu', 'char': 'ふ'},
315    {'name': 'he', 'char': 'へ'},
316    {'name': 'ho', 'char': 'ほ'},
317
318    {'name': 'ma', 'char': 'ま'},
319    {'name': 'mi', 'char': 'み'},
320    {'name': 'mu', 'char': 'む'},
321    {'name': 'me', 'char': 'め'},
322    {'name': 'mo', 'char': 'も'},
323
324    {'name': 'ya', 'char': 'や'},
325    {'name': 'yu', 'char': 'ゆ'},
326    {'name': 'yo', 'char': 'よ'},
327    {'name': 'xya', 'char': 'ゃ'},
328    {'name': 'xyu', 'char': 'ゅ'},
329    {'name': 'xyo', 'char': 'ょ'},
330
331    {'name': 'ra', 'char': 'ら'},
332    {'name': 'ri', 'char': 'り'},
333    {'name': 'ru', 'char': 'る'},
334    {'name': 're', 'char': 'れ'},
335    {'name': 'ro', 'char': 'ろ'},
336
337    {'name': 'wa', 'char': 'わ'},
338    {'name': 'wo', 'char': 'を'},
339    {'name': 'nn', 'char': 'ん'},
340    ]
341
342_TWELVEKEYS_KANA_SUPPORT_KEY_DEFINITIONS = [
343    {'name': '01',
344     'chars': ('あ', 'い', 'う', 'え', 'お')},
345    {'name': '02',
346     'chars': ('か', 'き', 'く', 'け', 'こ')},
347    {'name': '03',
348     'chars': ('さ', 'し', 'す', 'せ', 'そ')},
349    {'name': '04',
350     'chars': ('た', 'ち', 'つ', 'て', 'と')},
351    {'name': '05',
352     'chars': ('な', 'に', 'ぬ', 'ね', 'の')},
353    {'name': '06',
354     'chars': ('は', 'ひ', 'ふ', 'へ', 'ほ')},
355    {'name': '07',
356     'chars': ('ま', 'み', 'む', 'め', 'も')},
357    {'name': '08',
358     'chars': ('や', '(', 'ゆ', ')', 'よ')},
359    {'name': '09',
360     'chars': ('ら', 'り', 'る', 'れ', 'ろ')},
361    # 10: "大小" key(twelvekeys__kana__10.svg): Will be modified directly
362    {'name': '11',
363     'chars': ('わ', 'を', 'ん', 'ー', '〜')},
364    # 12: Needs another template. Defined bellow.
365    ]
366
367_TWELVEKEYS_KANA_SUPPORT_12_KEY_DEFINITION = [
368    {'name': '12',
369     'chars': ('、', '。', '?', '!', '…')}
370    ]
371
372_TWELVEKEYS_KANA_SUPPORT_POPUP_KEY_DEFINITIONS = [
373    {'name': '01', 'chars': ('あ', 'い', 'う', 'え', 'お')},
374    {'name': '02', 'chars': ('か', 'き', 'く', 'け', 'こ')},
375    {'name': '03', 'chars': ('さ', 'し', 'す', 'せ', 'そ')},
376    {'name': '04', 'chars': ('た', 'ち', 'つ', 'て', 'と')},
377    {'name': '05', 'chars': ('な', 'に', 'ぬ', 'ね', 'の')},
378    {'name': '06', 'chars': ('は', 'ひ', 'ふ', 'へ', 'ほ')},
379    {'name': '07', 'chars': ('ま', 'み', 'む', 'め', 'も')},
380    {'name': '08', 'chars': ('や', '(', 'ゆ', ')', 'よ')},
381    {'name': '09', 'chars': ('ら', 'り', 'る', 'れ', 'ろ')},
382    # 10: "大小" key: twelvekeys__kana__10.svg will be used.
383    # Please check mozc/android/resources/res/xml/
384    {'name': '11', 'chars': ('わ', 'を', 'ん', 'ー', '〜')},
385    {'name': '12', 'chars': ('、', '。', '?', '!', '…')},
386    ]
387
388_TWELVEKEYS_NUMBER_KEY_DEFINITIONS = [
389    {'name': 'zero', 'char': '0'},
390    {'name': 'one', 'char': '1'},
391    {'name': 'two', 'char': '2'},
392    {'name': 'three', 'char': '3'},
393    {'name': 'four', 'char': '4'},
394    {'name': 'five', 'char': '5'},
395    {'name': 'six', 'char': '6'},
396    {'name': 'seven', 'char': '7'},
397    {'name': 'eight', 'char': '8'},
398    {'name': 'nine', 'char': '9'},
399    {'name': 'asterisk', 'char': '*'},
400    {'name': 'number_sign', 'char': '#'},
401    ]
402
403_TWELVEKEYS_NUMBER_FUNCTION_KEY_DEFINITIONS = [
404    {'name': 'comma', 'char': ','},
405    {'name': 'equals_sign', 'char': '='},
406    {'name': 'full_stop', 'char': '.'},
407    {'name': 'hyphen_minus', 'char': '-'},
408    {'name': 'plus_sign', 'char': '+'},
409    {'name': 'solidus', 'char': '/'},
410    ]
411
412_TWELVEKEYS_OTHER_POPUP_DEFINITIONS = [
413    # number keyboard
414    {'name': 'low_line', 'char': '_'},
415    {'name': 'quotation_mark', 'char': '"'},
416    {'name': 'colon', 'char': ':'},
417    {'name': 'semicolon', 'char': ';'},
418    # 12keys and godan keyboard
419    {'name': 'ideographic_comma', 'char': '、'},
420    {'name': 'ideographic_full_stop', 'char': '。'},
421    # godan keyboard
422    {'name': 'fullwidth_solidus', 'char': '/'},
423    {'name': 'fullwidth_colon', 'char': ':'},
424    {'name': 'katakana_middle_dot', 'char': '・'},
425    {'name': 'left_corner_bracket', 'char': '「'},
426    {'name': 'right_corner_bracket', 'char': '」'},
427    ]
428
429def GenerateQwertyAlphabetKeyDefinitions():
430  """Returns key definitions for qwerty alphabet keys."""
431  key_definitions = []
432  for i in range(ord('z') - ord('a') + 1):
433    small = chr(i + ord('a'))
434    capital = chr(i + ord('A'))
435    support = _QWERTY_SUPPORT_KEY_MAPPING.get(small, '')
436    key_definitions.append({
437        'name': 'latin_capital_letter_' + small,
438        'char': capital,
439        'support': support})
440    key_definitions.append({
441        'name': 'latin_small_letter_' + small,
442        'char': small,
443        'support': support})
444  return key_definitions
445
446
447def GenerateTwelvekeysAlphabetKeyDefinitions():
448  """Returns key definitions for qwerty alphabet keys."""
449  key_definitions = []
450  for i in range(ord('z') - ord('a') + 1):
451    small = chr(i + ord('a'))
452    capital = chr(i + ord('A'))
453    key_definitions.append({
454        'name': 'latin_' + small,
455        'char': capital})
456  return key_definitions
457
458
459def ExpandAndWriteSupportKeys(template_file, output_file_basename, output_dir,
460                              key_definition_list):
461  """Generates and writes keyicons for support keys from template.
462
463  We will expand keyicons for each flick directions.
464  For example, we will generate XX_center, XX_left, XX_up, XX_right, XX_down
465  adding to XX_release (default, no highlight).
466  Expanded keyicons have highlighted key character corresponding to the flick
467  direction.
468
469  Args:
470    template_file: Path for template file.
471    output_file_basename: A string indicating the base name of the output.
472    output_dir: Destination directory path.
473    key_definition_list: Key definition list.
474  """
475  for key_definition in key_definition_list:
476    assert len(key_definition['chars']) == len(_REPLACE_CHARS)
477
478    with open(template_file) as template_f:
479      template = template_f.read()
480
481    for i, pattern in enumerate(_REPLACE_CHARS):
482      template = template.replace(pattern, key_definition['chars'][i])
483
484    for highlight_index, name_suffix in enumerate(_EXPAND_SUPPORT_SUFFIXES):
485      if (highlight_index < len(key_definition['chars']) and
486          not key_definition['chars'][highlight_index]):
487        # We don't need highlighted key for non-existent support.
488        continue
489
490      keytop = template
491      for i, id_pattern in enumerate(_REPLACE_ID_SUFFIXEX):
492        default_id_suffix = ('' if 'id_suffixes' not in key_definition
493                             else key_definition['id_suffixes'][i])
494        id_suffix = default_id_suffix if i != highlight_index else '-highlight'
495        keytop = keytop.replace(id_pattern, id_suffix)
496
497      file_name = '%s__%s_%s.svg' % (output_file_basename,
498                                     key_definition['name'],
499                                     name_suffix)
500      with open(os.path.join(output_dir, file_name), 'w') as write_f:
501        write_f.write(keytop)
502
503
504def WriteSupportPopupKeys(
505    template_file, output_file_basename, output_dir, key_definition_list):
506  """Generates and writes keyicons for support popup from template.
507
508  Support popup keys may have 5 key characters to be replaced.
509
510  Args:
511    template_file: Path for template file.
512    output_file_basename: A string indicating the base name of the output.
513    output_dir: Destination directory path.
514    key_definition_list: Key definition list.
515  """
516  for key_definition in key_definition_list:
517    assert len(key_definition['chars']) == len(_REPLACE_CHARS)
518
519    with open(template_file) as template_f:
520      keytop = template_f.read()
521    for i, pattern in enumerate(_REPLACE_CHARS):
522      keytop = keytop.replace(pattern, key_definition['chars'][i])
523
524    file_name = '%s__%s.svg' % (output_file_basename,
525                                key_definition['name'])
526    with open(os.path.join(output_dir, file_name), 'w') as write_f:
527      write_f.write(keytop)
528
529
530def WriteKeys(
531    template_file, output_file_basename, output_dir, key_definition_list):
532  """Generates and writes keyicons.
533
534  Keys have 1 key character to be replaced.
535
536  Args:
537    template_file: Path for template file.
538    output_file_basename: A string indicating the base name of the output.
539    output_dir: Destination directory path.
540    key_definition_list: Key definition list.
541  """
542  for key_definition in key_definition_list:
543    assert key_definition['char']
544
545    with open(template_file) as template_f:
546      keytop = template_f.read()
547    keytop = keytop.replace(_REPLACE_CHAR, key_definition['char'])
548    keytop = keytop.replace(_REPLACE_CHAR_QWERTY_SUPPORT,
549                            key_definition.get('support', ''))
550
551    file_name = '%s__%s.svg' % (output_file_basename,
552                                key_definition['name'])
553    with open(os.path.join(output_dir, file_name), 'w') as write_f:
554      write_f.write(keytop)
555
556
557def Transform(input_dir, output_dir):
558  """Transforms template .svg files into output_dir."""
559  ExpandAndWriteSupportKeys(
560      os.path.join(input_dir, 'godan__kana__support__template.svg'),
561      'godan__kana__support',
562      output_dir,
563      _GODAN_KANA_SUPPORT_KEY_DEFINITIONS)
564
565  ExpandAndWriteSupportKeys(
566      os.path.join(input_dir, 'godan__kana__support__12__template.svg'),
567      'godan__kana__support',
568      output_dir,
569      _GODAN_KANA_SUPPORT_12_KEY_DEFINITION)
570
571  WriteSupportPopupKeys(
572      os.path.join(input_dir, 'support__popup__template.svg'),
573      'godan__kana__support__popup',
574      output_dir,
575      _GODAN_KANA_SUPPORT_POPUP_KEY_DEFINITIONS)
576
577  qwerty_definitions = (
578      _QWERTY_KEYICON_KEY_DEFINITIONS + GenerateQwertyAlphabetKeyDefinitions())
579
580  WriteKeys(
581      os.path.join(input_dir, 'qwerty__keyicon__template.svg'),
582      'qwerty__keyicon',
583      output_dir,
584      qwerty_definitions)
585
586  WriteKeys(
587      os.path.join(input_dir, 'qwerty__popup__template.svg'),
588      'qwerty__popup',
589      output_dir,
590      [x for x in qwerty_definitions if x.get('support', '')])
591
592  WriteKeys(
593      os.path.join(input_dir, 'twelvekeys__popup__template.svg'),
594      'twelvekeys__popup',
595      output_dir,
596      GenerateTwelvekeysAlphabetKeyDefinitions())
597
598  WriteKeys(
599      os.path.join(input_dir, 'twelvekeys__alphabet__template.svg'),
600      'twelvekeys__alphabet',
601      output_dir,
602      _TWELVEKEYS_ALPHABET_KEY_DEFINITIONS)
603
604  WriteKeys(
605      os.path.join(input_dir, 'twelvekeys__alphabet__popup__template.svg'),
606      'twelvekeys__alphabet__popup',
607      output_dir,
608      _TWELVEKEYS_ALPHABET_KEY_DEFINITIONS)
609
610  ExpandAndWriteSupportKeys(
611      os.path.join(input_dir,
612                   'twelvekeys__alphabet__support__template.svg'),
613      'twelvekeys__alphabet__support',
614      output_dir,
615      _TWELVEKEYS_ALPHABET_SUPPORT_KEY_DEFINITIONS)
616
617  WriteSupportPopupKeys(
618      os.path.join(input_dir, 'support__popup__template.svg'),
619      'twelvekeys__alphabet__support__popup',
620      output_dir,
621      _TWELVEKEYS_ALPHABET_SUPPORT_POPUP_KEY_DEFINITIONS)
622
623  WriteKeys(
624      os.path.join(input_dir, 'twelvekeys__kana__keyicon__template.svg'),
625      'twelvekeys__kana__keyicon',
626      output_dir,
627      _TWELVEKEYS_KANA_KEYICON_KEY_DEFINITIONS)
628
629  WriteKeys(
630      os.path.join(input_dir, 'twelvekeys__popup__template.svg'),
631      'twelvekeys__popup',
632      output_dir,
633      _TWELVEKEYS_KANA_KEYICON_KEY_DEFINITIONS)
634
635  ExpandAndWriteSupportKeys(
636      os.path.join(input_dir, 'twelvekeys__kana__support__template.svg'),
637      'twelvekeys__kana__support',
638      output_dir,
639      _TWELVEKEYS_KANA_SUPPORT_KEY_DEFINITIONS)
640
641  ExpandAndWriteSupportKeys(
642      os.path.join(input_dir,
643                   'twelvekeys__kana__support__12__template.svg'),
644      'twelvekeys__kana__support',
645      output_dir,
646      _TWELVEKEYS_KANA_SUPPORT_12_KEY_DEFINITION)
647
648  WriteSupportPopupKeys(
649      os.path.join(input_dir, 'support__popup__template.svg'),
650      'twelvekeys__kana__support__popup',
651      output_dir,
652      _TWELVEKEYS_KANA_SUPPORT_POPUP_KEY_DEFINITIONS)
653
654  WriteKeys(
655      os.path.join(input_dir, 'twelvekeys__number__template.svg'),
656      'twelvekeys__number',
657      output_dir,
658      _TWELVEKEYS_NUMBER_KEY_DEFINITIONS)
659
660  WriteKeys(
661      os.path.join(input_dir, 'twelvekeys__popup__template.svg'),
662      'twelvekeys__popup',
663      output_dir,
664      _TWELVEKEYS_NUMBER_KEY_DEFINITIONS)
665
666  WriteKeys(
667      os.path.join(input_dir,
668                   'twelvekeys__number__function__template.svg'),
669      'twelvekeys__number__function',
670      output_dir,
671      _TWELVEKEYS_NUMBER_FUNCTION_KEY_DEFINITIONS)
672
673  WriteKeys(
674      os.path.join(input_dir,
675                   'twelvekeys__popup__template.svg'),
676      'twelvekeys__popup',
677      output_dir,
678      _TWELVEKEYS_NUMBER_FUNCTION_KEY_DEFINITIONS)
679
680  WriteKeys(
681      os.path.join(input_dir, 'twelvekeys__popup__template.svg'),
682      'twelvekeys__popup',
683      output_dir,
684      _TWELVEKEYS_OTHER_POPUP_DEFINITIONS)
685
686
687def ParseOptions():
688  parser = optparse.OptionParser()
689  parser.add_option('--input_dir', dest='input_dir',
690                    help='Input directory containing source svg files.')
691  parser.add_option('--output_dir', dest='output_dir',
692                    help='Output directory for generated svg files.')
693  parser.add_option('--output_zip', dest='output_zip',
694                    help='Output zip file for generated svg files.')
695  return parser.parse_args()[0]
696
697
698def main():
699  options = ParseOptions()
700  if not options.input_dir:
701    logging.fatal('You must specify --input_dir.')
702    return
703  if not options.output_dir and not options.output_zip:
704    logging.fatal('You must specify --output_dir and/or --output_zip.')
705    return
706  if options.output_dir:
707    output_dir = options.output_dir
708    if not os.path.exists(output_dir):
709      os.makedirs(output_dir)
710  else:
711    output_dir = tempfile.mkdtemp()
712  Transform(options.input_dir, output_dir)
713  if options.output_zip:
714    with zipfile.ZipFile(options.output_zip, 'w', zipfile.ZIP_DEFLATED) as z:
715      for f in os.listdir(output_dir):
716        if os.path.isdir(f):
717          raise IOError('Restriction: output_dir should not include directory')
718        z.write(os.path.join(output_dir, f), f)
719
720if __name__ == '__main__':
721  main()
722