junglejourney
changeset 147:17d0a4c8fde5
Started working on additional materials.
| author | David Boddie <david@boddie.org.uk> |
|---|---|
| date | Mon Sep 12 23:23:09 2011 +0200 |
| parents | 8dcefb5a013d |
| children | b7c0ad00dfa3 |
| files | materials/make_packaging.py |
| diffstat | 1 files changed, 270 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/materials/make_packaging.py Mon Sep 12 23:23:09 2011 +0200 1.3 @@ -0,0 +1,270 @@ 1.4 +#!/usr/bin/env python 1.5 + 1.6 +import os, sys 1.7 +from PyQt4.QtCore import QSize 1.8 +from PyQt4.QtGui import * 1.9 + 1.10 + 1.11 +class Page: 1.12 + 1.13 + def __init__(self, size, objects): 1.14 + 1.15 + self.size = size 1.16 + self.objects = objects 1.17 + 1.18 + def render(self, image = None): 1.19 + 1.20 + if not image: 1.21 + image = QImage(QSize(*self.size), QImage.Format_RGB32) 1.22 + image.fill(qRgb(255,255,255)) 1.23 + 1.24 + x, y = 0, 0 1.25 + for obj in self.objects: 1.26 + 1.27 + x, y = obj.render(image, x, y) 1.28 + 1.29 + return image 1.30 + 1.31 +class TextBox: 1.32 + 1.33 + def __init__(self, bbox, text_items, follow = False): 1.34 + 1.35 + self.bbox = bbox 1.36 + self.text_items = text_items 1.37 + self.follow = follow 1.38 + 1.39 + def render(self, image, previous_x, previous_y): 1.40 + 1.41 + x, y, width, height = self.bbox 1.42 + 1.43 + if self.follow: 1.44 + y += previous_y 1.45 + 1.46 + p = QPainter() 1.47 + p.begin(image) 1.48 + p.setRenderHint(QPainter.TextAntialiasing) 1.49 + 1.50 + for text_item in self.text_items: 1.51 + 1.52 + for pieces, line_height in text_item.readline(width): 1.53 + 1.54 + for font, word_x, text in pieces: 1.55 + 1.56 + p.setFont(font) 1.57 + p.drawText(x + word_x, y, text) 1.58 + 1.59 + y += line_height 1.60 + 1.61 + p.end() 1.62 + 1.63 + return x, y 1.64 + 1.65 +class Text: 1.66 + 1.67 + def __init__(self, font, font_size, text, align = "left", style = None, 1.68 + weight = None): 1.69 + 1.70 + self.font = {"family": font, "size": font_size, "weight": weight, 1.71 + "style": style} 1.72 + self.text = text 1.73 + self.align = align 1.74 + 1.75 + self.parse_text() 1.76 + 1.77 + def parse_text(self): 1.78 + 1.79 + lines = self.text.split("\n") 1.80 + self.lines = [] 1.81 + 1.82 + for line in lines: 1.83 + 1.84 + words = [] 1.85 + for word in line.split(): 1.86 + 1.87 + words.append(Word(self.font, word)) 1.88 + 1.89 + self.lines.append(words) 1.90 + 1.91 + def readline(self, width): 1.92 + 1.93 + for line in self.lines: 1.94 + 1.95 + w = 0 1.96 + used = 0 1.97 + words = [] 1.98 + 1.99 + while w < len(line): 1.100 + 1.101 + word = line[w] 1.102 + word_width = word.width() 1.103 + 1.104 + if used + word_width <= width: 1.105 + # Add words while there is still space. 1.106 + used += word_width + word.space() 1.107 + words.append(word) 1.108 + w += 1 1.109 + 1.110 + elif words: 1.111 + # When out of space, yield the words on the line. 1.112 + yield self.format(words, width), self.height(words) 1.113 + 1.114 + used = 0 1.115 + words = [] 1.116 + 1.117 + else: 1.118 + # If no words will fit on the line, just add the first 1.119 + # word to the list. 1.120 + yield self.format([word], width), self.height(words) 1.121 + 1.122 + used = 0 1.123 + w += 1 1.124 + 1.125 + if words: 1.126 + yield self.format(words, width), self.height(words) 1.127 + elif not line: 1.128 + yield [], self.line_height()/2 1.129 + 1.130 + def format(self, words, width): 1.131 + 1.132 + output = [] 1.133 + 1.134 + if len(words) == 0: 1.135 + spacing = 0 1.136 + elif self.align == "justify": 1.137 + # Full justify the text. 1.138 + total_width = sum(map(lambda word: word.width(), words)) 1.139 + spacing = (width - total_width)/float(len(words)) 1.140 + else: 1.141 + spacing = None 1.142 + 1.143 + x = 0 1.144 + for word in words: 1.145 + 1.146 + output.append((word.font(), x, word.text)) 1.147 + x += word.width() 1.148 + if spacing is not None: 1.149 + x += spacing 1.150 + else: 1.151 + x += word.space() 1.152 + 1.153 + return output 1.154 + 1.155 + def height(self, words): 1.156 + 1.157 + return max(map(lambda word: word.height(), words)) 1.158 + 1.159 + def line_height(self): 1.160 + 1.161 + font = QFont(self.font["family"]) 1.162 + font.setPixelSize(self.font["size"]) 1.163 + if self.font["weight"] == "bold": 1.164 + font.setWeight(QFont.Bold) 1.165 + if self.font["style"] == "italic": 1.166 + font.setItalic(True) 1.167 + 1.168 + metrics = QFontMetrics(font) 1.169 + return metrics.height() 1.170 + 1.171 +class Word: 1.172 + 1.173 + def __init__(self, font, text): 1.174 + 1.175 + self._font = font 1.176 + self.text = text 1.177 + 1.178 + def font(self): 1.179 + 1.180 + font = QFont(self._font["family"]) 1.181 + font.setPixelSize(self._font["size"]) 1.182 + if self._font["weight"] == "bold": 1.183 + font.setWeight(QFont.Bold) 1.184 + if self._font["style"] == "italic": 1.185 + font.setItalic(True) 1.186 + return font 1.187 + 1.188 + def width(self): 1.189 + 1.190 + metrics = QFontMetrics(self.font()) 1.191 + return metrics.width(self.text) 1.192 + 1.193 + def height(self): 1.194 + 1.195 + metrics = QFontMetrics(self.font()) 1.196 + return metrics.height() 1.197 + 1.198 + def space(self): 1.199 + 1.200 + metrics = QFontMetrics(self.font()) 1.201 + return metrics.width(" ") 1.202 + 1.203 + 1.204 +if __name__ == "__main__": 1.205 + 1.206 + app = QApplication(sys.argv) 1.207 + 1.208 + if len(app.arguments()) != 2: 1.209 + 1.210 + sys.stderr.write("Usage: %s <output directory>\n" % app.arguments()[0]) 1.211 + sys.exit(1) 1.212 + 1.213 + output_dir = sys.argv[1] 1.214 + 1.215 + if not os.path.exists(output_dir): 1.216 + os.mkdir(output_dir) 1.217 + 1.218 + pages = [ 1.219 + Page((800, 1000), 1.220 + [TextBox((80, 40, 640, 1120), 1.221 + [Text("FreeSerif", 24, "Jungle Journey\n", weight = "bold"), 1.222 + Text("FreeSerif", 24, 1.223 + "The last flames of the campfire fade to glowing embers and I am alone. " 1.224 + "My recent acquaintances, their packs and paraphernalia have gone, leaving " 1.225 + "me stranded deep in the heart of this jungle realm. Clouds momentarily " 1.226 + "sweep the cold face of the moon and I perceive the clicks, whistles and " 1.227 + "cries of creatures in the hot air that cloaks this place. Desperately, I " 1.228 + "try to stay my panic and remember those fragments of wilderness craft " 1.229 + "learned and unlearned many years ago.\n")]), 1.230 + TextBox((120, 0, 560, 0), 1.231 + [Text("FreeSerif", 24, 1.232 + "Choose your weapon carefully,\n" 1.233 + "Get ready for a fight.\n" 1.234 + "The jungle can be dangerous\n" 1.235 + "If you go there at night.\n" 1.236 + "There's time to pick up treasure,\n" 1.237 + "But no time to stop and stare.\n" 1.238 + "If you don't find the hidden gate\n" 1.239 + "You won't get out of there.\n", style = "italic")], 1.240 + follow = True), 1.241 + TextBox((80, 0, 640, 0), 1.242 + [Text("FreeSerif", 24, 1.243 + "Hopeless, I scramble to my feet, reaching for any weapon still left to me. " 1.244 + "Struggling through the dense undergrowth, I search for signs of a track or " 1.245 + "trail. At first glance, paths that seemed to lead to safety turn out to be " 1.246 + "impassable, overgrown by tangled and twisted vines. I remember the words of " 1.247 + "an old teacher:\n")], 1.248 + follow = True), 1.249 + TextBox((120, 0, 560, 0), 1.250 + [Text("FreeSerif", 22, 1.251 + u'\u201cDo not be tempted to use fire to make your way. ' 1.252 + 'Many a traveller has strayed from the path, using fire to blaze a trail, ' 1.253 + 'only to reach a dead end. Trying to return, they find that the jungle ' 1.254 + 'has grown back. Those who are desperate enough will even seek out ' 1.255 + u'forgotten routes when the way home is in sight.\u201d\n')], 1.256 + follow = True), 1.257 + TextBox((80, 0, 640, 0), 1.258 + [Text("FreeSerif", 24, 1.259 + "Sensing my presence, obscene creatures emerge from the darkness, hungry " 1.260 + "for prey. Only through skill and luck am I able to dispatch them back " 1.261 + "into the shadows. Even though I know I must journey deeper into this " 1.262 + "uncharted land to find the way home, the thought of vengeance drives me on.")], 1.263 + follow = True) 1.264 + ])] 1.265 + 1.266 + i = 0 1.267 + for page in pages: 1.268 + 1.269 + path = os.path.join(output_dir, "page-%i.png" % i) 1.270 + image = page.render() 1.271 + image.save(path) 1.272 + 1.273 + sys.exit()
