OLD | NEW |
| (Empty) |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 """Presubmit script for Chromium browser resources. | |
6 | |
7 See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts | |
8 for more details about the presubmit API built into gcl/git cl, and see | |
9 http://www.chromium.org/developers/web-development-style-guide for the rules | |
10 we're checking against here. | |
11 """ | |
12 | |
13 | |
14 import os | |
15 import struct | |
16 | |
17 | |
18 class InvalidPNGException(Exception): | |
19 pass | |
20 | |
21 | |
22 class ResourceScaleFactors(object): | |
23 """Verifier of image dimensions for Chromium resources. | |
24 | |
25 This class verifies the image dimensions of resources in the various | |
26 resource subdirectories. | |
27 | |
28 Attributes: | |
29 paths: An array of tuples giving the folders to check and their | |
30 relevant scale factors. For example: | |
31 | |
32 [(100, 'default_100_percent'), (200, 'default_200_percent')] | |
33 """ | |
34 | |
35 def __init__(self, input_api, output_api, paths): | |
36 """ Initializes ResourceScaleFactors with paths.""" | |
37 self.input_api = input_api | |
38 self.output_api = output_api | |
39 self.paths = paths | |
40 | |
41 def RunChecks(self): | |
42 """Verifies the scale factors of resources being added or modified. | |
43 | |
44 Returns: | |
45 An array of presubmit errors if any images were detected not | |
46 having the correct dimensions. | |
47 """ | |
48 def ImageSize(filename): | |
49 with open(filename, 'rb', buffering=0) as f: | |
50 data = f.read(24) | |
51 if data[:8] != '\x89PNG\r\n\x1A\n' or data[12:16] != 'IHDR': | |
52 raise InvalidPNGException | |
53 return struct.unpack('>ii', data[16:24]) | |
54 | |
55 # Returns a list of valid scaled image sizes. The valid sizes are the | |
56 # floor and ceiling of (base_size * scale_percent / 100). This is equivalent | |
57 # to requiring that the actual scaled size is less than one pixel away from | |
58 # the exact scaled size. | |
59 def ValidSizes(base_size, scale_percent): | |
60 return sorted(set([(base_size * scale_percent) / 100, | |
61 (base_size * scale_percent + 99) / 100])) | |
62 | |
63 repository_path = self.input_api.os_path.relpath( | |
64 self.input_api.PresubmitLocalPath(), | |
65 self.input_api.change.RepositoryRoot()) | |
66 results = [] | |
67 | |
68 # Check for affected files in any of the paths specified. | |
69 affected_files = self.input_api.AffectedFiles(include_deletes=False) | |
70 files = [] | |
71 for f in affected_files: | |
72 for path_spec in self.paths: | |
73 path_root = self.input_api.os_path.join( | |
74 repository_path, path_spec[1]) | |
75 if (f.LocalPath().endswith('.png') and | |
76 f.LocalPath().startswith(path_root)): | |
77 # Only save the relative path from the resource directory. | |
78 relative_path = self.input_api.os_path.relpath(f.LocalPath(), | |
79 path_root) | |
80 if relative_path not in files: | |
81 files.append(relative_path) | |
82 | |
83 corrupt_png_error = ('Corrupt PNG in file %s. Note that binaries are not ' | |
84 'correctly uploaded to the code review tool and must be directly ' | |
85 'submitted using the dcommit command.') | |
86 for f in files: | |
87 base_image = self.input_api.os_path.join(self.paths[0][1], f) | |
88 if not os.path.exists(base_image): | |
89 results.append(self.output_api.PresubmitError( | |
90 'Base image %s does not exist' % self.input_api.os_path.join( | |
91 repository_path, base_image))) | |
92 continue | |
93 try: | |
94 base_dimensions = ImageSize(base_image) | |
95 except InvalidPNGException: | |
96 results.append(self.output_api.PresubmitError(corrupt_png_error % | |
97 self.input_api.os_path.join(repository_path, base_image))) | |
98 continue | |
99 # Find all scaled versions of the base image and verify their sizes. | |
100 for i in range(1, len(self.paths)): | |
101 image_path = self.input_api.os_path.join(self.paths[i][1], f) | |
102 if not os.path.exists(image_path): | |
103 continue | |
104 # Ensure that each image for a particular scale factor is the | |
105 # correct scale of the base image. | |
106 try: | |
107 scaled_dimensions = ImageSize(image_path) | |
108 except InvalidPNGException: | |
109 results.append(self.output_api.PresubmitError(corrupt_png_error % | |
110 self.input_api.os_path.join(repository_path, image_path))) | |
111 continue | |
112 for dimension_name, base_size, scaled_size in zip( | |
113 ('width', 'height'), base_dimensions, scaled_dimensions): | |
114 valid_sizes = ValidSizes(base_size, self.paths[i][0]) | |
115 if scaled_size not in valid_sizes: | |
116 results.append(self.output_api.PresubmitError( | |
117 'Image %s has %s %d, expected to be %s' % ( | |
118 self.input_api.os_path.join(repository_path, image_path), | |
119 dimension_name, | |
120 scaled_size, | |
121 ' or '.join(map(str, valid_sizes))))) | |
122 return results | |
OLD | NEW |