-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathSimulatorGeneratorImage.py
executable file
·278 lines (228 loc) · 10.2 KB
/
SimulatorGeneratorImage.py
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#!/usr/bin/env python
# built-ins
import os
import sys
import mimetypes
import datetime
import subprocess
import random
import tempfile
# site-packages
import requests
# global constants
BASE_PATH = os.path.dirname(os.path.abspath( __file__ ))
# local
sys.path.insert(0, os.path.join(BASE_PATH, "lib"))
import titlecase
def cap(value, maxVal):
if (value < maxVal):
return value
else:
return maxVal
def wpl(totalwords, current=None):
if (current == None):
current = []
if (totalwords == 0):
return current
if (totalwords == 1):
current.append(1)
return current
if (totalwords % 3 == 0):
return current + [3]*(totalwords/3)
current.append(2)
return wpl(totalwords-2, current)
def getImageFor(searchTerm, safeSearchLevel="moderate", referer=None):
is_params = {
"v" : "1.0",
"q" : searchTerm,
"imgType" : "photo",
"imgsz" : "small|medium|large|xlarge|xxlarge|huge",
"rsz": 8,
"safe" : safeSearchLevel
}
is_headers = {}
if referer:
is_headers["Referer"] = referer
is_URL = "https://ajax.googleapis.com/ajax/services/search/images"
imageResults = requests.get(is_URL, params=is_params, headers=is_headers).json()
if (imageResults == None or 'responseData' not in imageResults or imageResults['responseData'] == None):
sys.stderr.write("No response data in image search for %s. JSON:\n%s\n" % (searchTerm, imageResults))
sys.stderr.write("Google is mad at you for searching too often. Chill out for a while and try again.\n")
return None
imageData = []
for image in imageResults['responseData']['results']:
imageData.append(
{
"url" : image['url'],
"h" : int(image['height']),
"w" : int(image['width']),
"size" : int(image['width']) * int(image['height'])
}
)
# find the largest image
# imageData.sort(reverse=True, key=lambda img: img['size'])
# just pick a random one after throwing out anything smaller than 150x150
imageData = filter(lambda x: x["size"] > 22500, imageData)
random.shuffle(imageData)
mimetypes.init()
for img in imageData:
try:
r = requests.head(img['url'])
if not r.ok:
# can't download for whatever reason
continue
except:
# requests library puked
continue
try:
extension = mimetypes.guess_extension(r.headers['Content-Type'])
except KeyError, e:
sys.stderr.write("Couldn't find content-type header: %s" % str(r.headers))
extension = ""
if (extension == ".jpe") : extension = ".jpg"
if (extension == None) : extension = ""
localFileName = "base_image-%s%s" % (datetime.datetime.now().strftime("%Y-%m-%d-%H%M.%f"), extension)
localFileName = os.path.join(tempfile.gettempdir(), localFileName)
imgResponse = requests.get(img['url'])
with open(localFileName, 'wb') as baseFile:
baseFile.write(imgResponse.content)
# check our work
if os.path.exists(localFileName):
cmdLine = ['identify', '-format', '%wx%h', localFileName]
dimensionString = subprocess.Popen(cmdLine, stdout=subprocess.PIPE).communicate()[0]
dimensions = dimensionString.split("x")
if (int(dimensions[0]) == img['w'] and int(dimensions[1]) == img['h']):
return localFileName
return None
def createBoxArt(jobTitle, year, inputFile, outputFile, maxSize=None, textPosition=None, font=None, deleteInputFile=False, log=False):
if textPosition == "TopRight": grav = "NorthEast"
elif textPosition == "TopLeft": grav = "NorthWest"
elif textPosition == "BottomRight": grav = "SouthEast"
elif textPosition == "BottomLeft": grav = "SouthWest"
else:
grav = random.choice(("NorthWest", "NorthEast", "SouthWest", "SouthEast"))
if grav[-4:] == "West":
align = "West"
else:
align = "East"
if font == None:
font = os.path.join(BASE_PATH, "data", "helvetica-ultra-compressed.ttf")
if not os.path.exists(font):
sys.stderr.write("WARNING: Couldn't find %s; text will probably look crappy.\n" % (font))
jobTitle = titlecase.titlecase(jobTitle)
wordlist = jobTitle.split()
wordsPerLine = wpl(len(wordlist))
jobTitle = ""
indent = " "
for wordCount in wordsPerLine:
while wordCount > 0:
jobTitle += wordlist.pop(0) + " "
wordCount -= 1
jobTitle += "\n"
if (align == "West"):
jobTitle += indent
indent += " "
# newlines to deal with the font overruning its rendering bounds;
# we trim the canvas in imagemagick anyway
jobTitle = "\n%sSimulator %i\n" % (jobTitle, year)
cmdLine = ['identify', '-format', '%wx%h', inputFile]
try:
dimensionString = subprocess.Popen(cmdLine, stdout=subprocess.PIPE).communicate()[0]
except TypeError, e:
sys.stderr.write("Couldn't get dimensions for %s\n" % inputFile)
return None
dimensions = map(int, dimensionString.split("x"))
if (dimensions[0] > dimensions[1]):
widthMultiplier = 0.65
else:
widthMultiplier = 0.95
offset = "+%i+%i" % (cap(dimensions[0] * .05, 20), cap(dimensions[1] * .05, 20))
command = [
"convert",
# generate large text that we'll scale down to fit the target image later
"-background", "none",
"-fill", "white",
"-stroke", "gray",
"-strokewidth", "3",
"-kerning", "-5",
"-font", font,
"-pointsize", "300",
"-gravity", align,
"-interline-spacing", "75",
# "-annotate", "+0+2", "'%s'" % jobTitle.encode("utf8"),
("label:%s" % jobTitle).encode("utf8"),
"-shear", "10x0", # since this font doesn't have true oblique / italics
"-trim", # remove the extra space added by the newline wrapping
# bring its size down so it can be overlaid into the base image
"-resize", "%ix%i" % (dimensions[0] * widthMultiplier, dimensions[1] * .95),
inputFile, "+swap", # take everything we just created and make it
# the thing that we're overlaying. inputFile
# becomes the target
"-gravity", grav,
"-geometry", offset,
"-composite",
"-quiet",
outputFile
]
if maxSize:
command.insert(-1, "-resize")
command.insert(-1, "%sx%s>" % maxSize)
if (log):
print("ImageMagick command: %s" % " ".join(command))
subprocess.call(command)
if (deleteInputFile):
os.remove(inputFile)
print "Output: %s" % (outputFile)
return outputFile
if __name__ == '__main__':
random.seed()
import argparse
description = """This program makes super rad box art for video games in the
exciting job simulation genre. Note that you must install ImageMagick
(http://www.imagemagick.org/) and the Python Requests library
(http://docs.python-requests.org/en/latest/) for this to work properly."""
parser = argparse.ArgumentParser(
description=description,
add_help=False
)
parser.add_argument("simulatorSubject", help="The topic for which you want to make simulator box art.")
parser.add_argument("-i", "--input-file", help="Path to the image file to use for the box art. If not specified, we'll do a Google Image Search for the subject.")
parser.add_argument("-o", "--output-file", help="Path to the image file to store the box art. (defaults: an image named \"boxart-[timestamp].png\" in the current directory)")
parser.add_argument("-w", "--max-width", help="Maximum width for the ouput image. Output images will be resized if they're wider than this.", type=int)
parser.add_argument("-h", "--max-height", help="Maximum height for the ouput image. Output images will be resized if they're taller than this.", type=int)
parser.add_argument("-p", "--text-position", help="Where the text should go on the image. Will pick randomly if not specified.", type=str, choices=["TopRight", "TopLeft", "BottomRight", "BottomLeft"])
parser.add_argument("-f", "--font-file", help="Path to the font file to use for the image text. Looks best with Helvetica Ultra Compressed, which you'll have to track down. :(")
parser.add_argument("-s", "--safe-search", help="The type of SafeSearch to use for Google Image Search. (default: %(default)s)", type=str, choices=["active", "moderate", "off"], default="moderate")
parser.add_argument("-y", "--year", help="The year to put in the box art. (default: random year)", type=int)
parser.add_argument("-b", "--min-year", help="The lowest year that will get randomly chosen. (default: %(default)s)", type=int, default=2007)
parser.add_argument("-t", "--max-year", help="The highest year that will get randomly chosen. (default: current year)", type=int, default=datetime.date.today().year)
parser.add_argument("-d", "--debug", help="Enables debug output. (default: off)", action="store_true")
parser.add_argument("--help", action="help", help="show this help message and exit")
args = parser.parse_args()
img = args.input_file
if not img:
img = getImageFor(args.simulatorSubject)
output = args.output_file
if not output:
output = "boxart-%s.png" % datetime.datetime.now().strftime("%Y-%m-%d-%H%M.%f")
year = args.year
if not year:
year = random.randint(args.min_year, args.max_year)
if (args.max_width or args.max_height):
w = str(args.max_width) if args.max_width else ""
h = str(args.max_height) if args.max_height else ""
max_size = (w, h)
else:
max_size = None
if (img):
createBoxArt(
titlecase.titlecase(args.simulatorSubject),
year,
img,
output,
maxSize=max_size,
textPosition=args.text_position,
font=args.font_file,
deleteInputFile=(args.input_file == None), # no input; working from Google Image Search; delete temp file
log=args.debug
)