1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 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 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
# Copyright 2015 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import fileinput
import glob
import optparse
import os
import re
import shlex
import sys
import textwrap
CANVAS_DIMENSIONS = "CANVAS_DIMENSIONS"
CPP_COMMENT_DELIMITER = "//"
REFERENCE_SIZE_DIP = 48
TEMPLATE_PLACEHOLDER = "TEMPLATE_PLACEHOLDER"
def Error(msg):
print(msg, file=sys.stderr)
sys.exit(1)
def CamelCase(name, suffix):
words = name.split("_")
words = [w.capitalize() for w in words]
return "k" + "".join(words) + suffix
def GetPathName(name, size=None):
return CamelCase(name, "{}Path".format(size) if size != None else "Path")
def GetRepListName(name):
return CamelCase(name, "RepList")
def GetIconName(name):
return CamelCase(name, "Icon")
def AddIconToDictionary(icon_file, new_icon, icon_size, icon_index):
if icon_size in icon_index:
Error("Duplicate icon of size {} found in {}.".format(icon_size, icon_file))
icon_index[icon_size] = "\n".join(new_icon)
return icon_index
def ExtractIconReps(icon_file_name):
"""Reads the contents of the given icon file and returns a dictionary of icon
sizes to vector commands for different icon representations stored in that
file.
Args:
icon_file_name: The file path of the icon file to read.
"""
with open(icon_file_name, "r") as icon_file:
icon_file_contents = icon_file.readlines()
current_icon_size = REFERENCE_SIZE_DIP
icon_sizes = []
current_icon_representation = []
icon_representations = {}
for line in icon_file_contents:
# Strip comments and empty lines.
line = line.partition(CPP_COMMENT_DELIMITER)[0].strip()
if not line:
continue
# Retrieve sizes specified by CANVAS_DIMENSIONS to ensure icons are added in
# sorted order by size descending.
if line.startswith(CANVAS_DIMENSIONS):
sizes = re.findall(r"\d+", line)
if len(sizes) != 1:
Error("Malformed {} line in {} - it should specify exactly one size."
.format(CANVAS_DIMENSIONS, icon_file_name))
icon_sizes.append(int(sizes[0]))
# All icons except the first / default icon must start with
# "CANVAS_DIMENSIONS", so rely on it here as a icon delimiter.
if current_icon_representation:
icon_representations = AddIconToDictionary(
icon_file_name, current_icon_representation, current_icon_size,
icon_representations)
current_icon_representation = []
current_icon_size = icon_sizes[-1]
current_icon_representation.append(line)
if current_icon_representation:
icon_representations = AddIconToDictionary(
icon_file_name, current_icon_representation, current_icon_size,
icon_representations)
if not icon_representations:
Error("Didn't find any icons in {}.".format(icon_file_name))
if len(icon_representations) != len(icon_sizes):
icon_sizes.insert(0, REFERENCE_SIZE_DIP)
if sorted(icon_sizes, reverse=True) != icon_sizes:
Error("The icons in {} should be sorted in descending order of size."
.format(icon_file_name))
return icon_representations
def AggregateVectorIcons(working_directory, file_list, output_cc, output_h):
"""Compiles all .icon files in a directory into two C++ files.
Args:
working_directory: The path to the directory that holds the .icon files
and C++ templates.
file_list: A file containing the list of vector icon files to process.
output_cc: The path that should be used to write the .cc file.
output_h: The path that should be used to write the .h file.
"""
# For each file in |file_list|, place it in |path_map| if its extension is
# .icon. This will map the icon's name to its path, e.g.,
# path_map['cat'] = 'foo/bar/cat.icon'.
icon_list = []
with open(file_list, "r") as f:
file_list_contents = f.read()
icon_list = shlex.split(file_list_contents)
path_map = {}
for icon_path in icon_list:
(icon_name, extension) = os.path.splitext(os.path.basename(icon_path))
if extension != ".icon":
Error("Only filenames " + icon_name + ".icon are allowed.")
if icon_name not in path_map:
path_map[icon_name] = icon_path
else:
Error("A vector icon with name '" + icon_name + "' already exists.")
# Generate the file vector_icons.h which declares a variable for each
# icon in |path_map|. The variable name is derived from the icon name by
# converting to camel case, prepending 'k', and appending 'Icon'. For
# example, the icon 'foo_bar' will have the variable name kFooBarIcon.
with open(os.path.join(working_directory, "vector_icons.h.template"),
"r") as input_header_template:
header_template_contents = input_header_template.readlines()
output_header = open(output_h, "w")
for line in header_template_contents:
if not TEMPLATE_PLACEHOLDER in line:
output_header.write(line)
continue
for icon in path_map:
(icon_name, extension) = os.path.splitext(
os.path.basename(path_map[icon]))
output_header.write("VECTOR_ICON_TEMPLATE_H({})\n".format(
GetIconName(icon_name)))
output_header.close()
# Copy the vector icon drawing commands from the .icon files, splitting them
# into their individual icon representations, and use them to generate
# vector_icons.cc, which defines the variables declared in vector_icons.h.
input_cc_template = open(
os.path.join(working_directory, "vector_icons.cc.template"))
cc_template_contents = input_cc_template.readlines()
input_cc_template.close()
output_cc = open(output_cc, "w")
for line in cc_template_contents:
if not TEMPLATE_PLACEHOLDER in line:
output_cc.write(line)
continue
for icon in path_map:
(icon_name, extension) = os.path.splitext(
os.path.basename(path_map[icon]))
icon_representations = ExtractIconReps(path_map[icon])
icon_representation_strings = []
for i, size in enumerate(sorted(icon_representations, reverse=True)):
vector_commands = icon_representations[size]
icon_path_name = GetPathName(icon_name, size if i != 0 else None)
# Store the vector-drawing commands for foo_bar.icon in the temporary
# variable kFooBarPath.
output_cc.write("VECTOR_ICON_REP_TEMPLATE({}, {})\n".format(
icon_path_name, vector_commands))
icon_representation_strings.append(
"{{{0}, std::size({0})}}".format(icon_path_name))
# Another temporary variable kFooBarRepList is used to create all the
# VectorIconReps inline, with a pointer to it in the final VectorIcon.
output_cc.write("VECTOR_ICON_TEMPLATE_CC({}, {}, {})\n".format(
GetRepListName(icon_name), GetIconName(icon_name),
", ".join(icon_representation_strings)))
output_cc.close()
def main():
parser = optparse.OptionParser()
parser.add_option("--working_directory",
help="The directory to look for template C++ as well as "
"icon files.")
parser.add_option("--file_list",
help="A response file containing the list of icon files "
"to be processed.")
parser.add_option("--output_cc",
help="The path to output the CC file to.")
parser.add_option("--output_h",
help="The path to output the header file to.")
(options, args) = parser.parse_args()
AggregateVectorIcons(options.working_directory,
options.file_list,
options.output_cc,
options.output_h)
if __name__ == "__main__":
main()
|