oxtools/oxposter/poster.py

156 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from __future__ import division
from optparse import OptionParser
import os
import sys
import Image
import ImageDraw
from oxlib import truncateString, wrapString
data_root = os.path.join(os.path.dirname(__file__), 'data')
def main():
parser = OptionParser()
parser.add_option('-o', '--oxdb', dest='oxdb', help='0xdb Id')
parser.add_option('-i', '--imdb', dest='imdb', help='IMDb Id')
parser.add_option('-t', '--title', dest='title', help='Movie title')
parser.add_option('-d', '--director', dest='director', help='Director(s)')
parser.add_option('-f', '--frame', dest='frame', help='Poster frame (image file to be read)')
parser.add_option('-p', '--poster', dest='poster', help='Poster (image file to be written)')
parser.add_option('-r', '--restricted', action='store_true', dest='restricted', help='If set, poster frame will have overlay')
(opts, args) = parser.parse_args()
if None in (opts.oxdb, opts.title, opts.director, opts.poster):
parser.print_help()
sys.exit()
opts.oxdb = '%s%s' % (opts.oxdb[:2], opts.oxdb[2:8].upper())
opts.title = opts.title.decode('utf-8')
opts.director = opts.director.decode('utf-8')
if opts.imdb == None:
id = opts.oxdb
else:
id = opts.imdb
posterWidth = 640
posterHeight = 1024
posterImage = Image.new('RGB', (posterWidth, posterHeight))
fontImage = Image.open(os.path.join(data_root, 'font.monaco.bold.png'))
oxdbImage = Image.open(os.path.join(data_root, 'logo.0xdb.large.png'))
# frame section
frameHeight = int(posterWidth * 10 / 16)
if opts.frame:
frameImage = Image.open(opts.frame)
if frameImage.size[0] / frameImage.size[1] > posterWidth / frameHeight:
# poster frame is too wide
frameImage = frameImage.resize((int(frameImage.size[0] * frameHeight / frameImage.size[1]), frameHeight), Image.ANTIALIAS)
crop = int((frameImage.size[0] - posterWidth) / 2)
frameImage = frameImage.crop((crop, 0, crop + posterWidth, frameHeight))
else:
# poster frame is not wide enough
frameImage = frameImage.resize((posterWidth, int(frameImage.size[1] * posterWidth / frameImage.size[0])), Image.ANTIALIAS)
crop = int((frameImage.size[1] - frameHeight) / 2)
frameImage = frameImage.crop((0, crop, posterWidth, crop + frameHeight))
posterImage.paste(frameImage, (0, 0, posterWidth, frameHeight))
else:
draw = ImageDraw.Draw(posterImage)
draw.polygon([(0, 0), (posterWidth, 0), (posterWidth, frameHeight), (0, frameHeight)], fill=(0, 0, 0))
for y in range(frameHeight):
for x in range(posterWidth):
if int((x + y + 54) / 128) % 2:
posterImage.putpixel((x, y), (32, 32, 32))
# restricted
if opts.restricted:
for y in range(frameHeight):
for x in range(posterWidth):
if int((x + y + 54) / 128) % 2:
rgb = posterImage.getpixel((x, y))
rgb = (int(rgb[0] / 2) + 128, int(rgb[1] / 2), int(rgb[2] / 2))
posterImage.putpixel((x, y), rgb)
# director section
colorHeight = int(posterHeight / 2);
draw = ImageDraw.Draw(posterImage)
draw.polygon([(0, frameHeight), (posterWidth, frameHeight), (posterWidth, colorHeight), (0, colorHeight)], fill=(0, 0, 0))
director = wrapString(opts.director, 36, '\n', True)
while len(director.split('\n')) > 3:
director = opts.director.split(', ')
director.pop()
opts.director = ', '.join(director)
director = wrapString(opts.director, 36, '\n', True)
posterMargin = 16
imagewrite(posterImage, director, posterMargin, colorHeight - 8 - len(director.split('\n')) * 32, fontImage, 32)
# title section
backgroundColor = getRGB(opts.oxdb)
draw.polygon([(0, colorHeight), (posterWidth, colorHeight), (posterWidth, posterHeight), (0, posterHeight)], fill=backgroundColor)
title = wrapString(opts.title, 24, '\n', True)
lines = title.split('\n')
if lines > 8:
# following line commented out since the only known case
# (http://0xdb.org/0071458) looks better without '...'
# lines[7] = truncateString(lines[7] + '...', 24)
title = '\n'.join(lines[:8])
offset = -6
posterimage = imagewrite(posterImage, title, posterMargin, colorHeight + posterMargin + offset, fontImage, 48)
offset = 12
posterimage = imagewrite(posterImage, id, posterMargin, posterHeight - posterMargin - 96 + offset, fontImage, 96)
# 0xdb logo
x = posterWidth - oxdbImage.size[0] - posterMargin
y = posterHeight - oxdbImage.size[1] - posterMargin
for dy in range(oxdbImage.size[1]):
for dx in range(oxdbImage.size[0]):
rgb = posterImage.getpixel((x + dx, y + dy))
bw = oxdbImage.getpixel((dx, dy))[0]
rgb = tuple(map(lambda x : x + bw, rgb))
posterImage.putpixel((x + dx, y + dy), rgb)
posterImage.save(opts.poster)
def getRGB(oxid):
i = int(int(oxid[2:8], 16) * 762 / 16777216)
if i < 127:
return (127, i, 0)
elif i < 254:
return (254 - i, 127, 0)
elif i < 381:
return (0, 127, i - 254)
elif i < 508:
return (0, 508 - i, 127)
elif i < 635:
return (i - 508, 0, 127)
else:
return (127, 0, 762 - i)
def imagewrite(posterImage, string, left, top, fontImage, charHeight):
x = left
y = top
fontWidth = int(fontImage.size[0] / 16)
fontHeight = int(fontImage.size[1] / 16)
charWidth = int(fontWidth * charHeight / fontHeight)
for i in range(len(string)):
char = string[i:i+1]
if char == '\n':
x = left
y += charHeight
else:
ascii = ord(char)
fontLeft = (ascii % 16) * fontWidth
fontTop = int(ascii / 16) * fontHeight
letterImage = fontImage.crop((fontLeft, fontTop, fontLeft + fontWidth, fontTop + fontHeight))
letterImage = letterImage.resize((charWidth, charHeight), Image.ANTIALIAS)
for dy in range(charHeight):
for dx in range(charWidth):
rgb = posterImage.getpixel((x + dx, y + dy))
bw = int(letterImage.getpixel((dx, dy))[0] / 2)
rgb = tuple(map(lambda x : x + bw, rgb))
posterImage.putpixel((x + dx, y + dy), rgb)
x += charWidth
return posterImage