1
2 """
3 Copyright 2009 Sébastien L. W. Baelde (contact : clic4@clic4.org)
4
5 This file is part of ImASCII.
6
7 ImASCII is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 any later version.
11
12 ImASCII is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with ImASCII. If not, see <http://www.gnu.org/licenses/>.
19 """
20
21 from PIL import Image, ImageDraw, ImageFont
22 from PyRTF import Document, Section, Renderer, Font, TEXT, LINE, Paragraph, Colour
23 import math, os
24
26 return file( '%s.rtf' % name, 'w' )
27
30 self.x = x
31 self.y = y
32
34 """ Classe gérant une couleur. """
36 """
37 Constructeur.
38
39 @param r: Couleur rouge (0-255)
40 @param v: Couleur vert (0-255)
41 @param b: Couleur bleu (0-255)
42 """
43 self.r = int(r)
44 self.v = int(v)
45 self.b = int(b)
46
48 return "r: "+ str(self.r) + " v: " + str(self.v) + " b: " + str(self.b)
49
51
52
53
54 if coul.r <= self.r + 35 and coul.r >= self.r - 35:
55 if coul.v <= self.v + 35 and coul.v >= self.v - 35:
56 if coul.b <= self.b + 35 and coul.b >= self.b - 35:
57 return True
58 else:
59 return False
60
62 """
63 Classe pour effectuer la conversion d'une image en texte.
64 """
66 """
67 Construction d'une table de référence pour les caractères comprenant la valeur moyenne pour chaque lettre.
68 Un espace est équivalent à un poids de 1.0 tandis qu'un caractère complètement noir aurait la valeur de 0.0
69
70 @param parent: Fenêtre parent.
71 """
72 self.parent = parent
73 self.listeCaracteresA = ['.', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
74 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
75 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
76 'X', 'Y', 'Z', ',', ';', ':', '?', '!', '+', '-', '*', '&', '/', '=', '(', ')', '<',
77 '>', '_', '[', ']', '{', '}', 'ä', '^', '|', 'ç', '§', '°', '#', '@', '%', '0',
78 '1', '2', '3', '4', '5', '6', '7', '8', '9', '\'', '\"', '~', '¢', '¬', '£', 'é', 'è']
79 self.listeCaracteresB = [' ', '.', 'o', ',', ';', ':', '?', '%', '!', '+', '-', '*', '(', ')', '<', '>', '_', '[', ']', '{', '}', '^', '°' ]
80 self.listeCaracteresPerso = []
81
82 self.listeCaracteres = self.listeCaracteresA
83
84
85 self.poidsNBCarac = {}
86
87 if os.name == 'nt':
88 self.police = 'Lucida Console'
89 else:
90 self.police = 'Bitstream Vera Sans Mono'
91
92 self.nomImage = "img/guttenberg.png"
93
94 self.nbrCarac = 120.0
95
96 self.tolerancePoids = 0.05
97
98 self.precisionTolerance = 100.0
99
100 self.caractereDefaut = '-'
101
102 self.calculerPoids()
103
105 """
106 Calculer le poids de chaque caractère, selon la liste de référence.
107
108 @keyword tailleCarac: Taille des caractères en pixels pour calculer le poids.
109 """
110 self.poidsNBCarac = {}
111 for c in self.listeCaracteres:
112 poids = 0.000001
113
114 font = ImageFont.truetype(self.parent.listeFonts[self.police], tailleCarac)
115 img = Image.new('RGB', (20, 20), (255, 255, 255))
116 carac = ImageDraw.Draw(img)
117 carac.text((0, 0), c, font=font, fill=(0, 0, 0) )
118
119 res = img.load()
120
121
122 for y in range(20):
123 for x in range(20):
124 pix = res[x, y]
125
126 p = ( pix[0]/255.0 + pix[1]/255.0 + pix[2]/255.0 ) / 3.0
127
128 poids += p
129
130 poidsMoyen = float(poids) / float( img.size[0] * img.size[1] )
131
132 self.poidsNBCarac[c] = poidsMoyen
133
134
135
136
137
138 pMin = 1.0
139 pMax = 0.00001
140
141 for c in self.poidsNBCarac:
142 if self.poidsNBCarac[c] < pMin:
143 pMin = self.poidsNBCarac[c]
144 if self.poidsNBCarac[c] > pMax:
145 pMax = self.poidsNBCarac[c]
146
147 for c in self.poidsNBCarac:
148 self.poidsNBCarac[c] = ( self.poidsNBCarac[c]-pMin ) / ( pMax-pMin)
149
150
152 """
153 Convertir l'image en texte.
154
155 Charge une image et la découpe en damier. Pour chaque case, la
156 valeur moyenne des pixels est calculée.
157
158 @keyword tailleCarac: Taille des caractères en pixels.
159 """
160
161 self.img = Image.open(self.nomImage)
162 self.img = self.img.convert("RGB")
163
164
165 self.img256 = self.img.convert("P")
166
167 tableCouleur = self.img256.resize((256, 1))
168 tableCouleur.putdata(range(256))
169 tableCouleur = tableCouleur.convert("RGB").getdata()
170
171 self.dimImg = Vecteur2D(self.img.size[0], self.img.size[1])
172
173 if self.dimImg.x < self.nbrCarac:
174 ratio = self.nbrCarac / self.dimImg.x
175 self.img = self.img.resize( ( int(self.dimImg.x*ratio), int(self.dimImg.y*ratio) ), Image.ANTIALIAS )
176 self.dimImg = Vecteur2D(self.img.size[0], self.img.size[1])
177
178 Ri = float(self.dimImg.x) / float(self.dimImg.y)
179
180
181 font = ImageFont.truetype( self.parent.listeFonts[self.police], tailleCarac*2)
182 tempo = Image.new('RGB', (20, 20), (255, 255, 255))
183 car = ImageDraw.Draw(tempo)
184 car.text((0, 0), 'S', font=font, fill=(0, 0, 0) )
185 dim = car.textsize('s', font=font)
186 ratioFont = float(dim[1]) / float(dim[0])
187
188 i = self.dimImg.x/self.nbrCarac
189 self.vNbrCarac = Vecteur2D( self.nbrCarac, self.nbrCarac / Ri )
190 self.vNbrCarac.y = self.vNbrCarac.y / ratioFont
191 j = self.dimImg.y / self.vNbrCarac.y
192 self.dimDamier = Vecteur2D( i, j )
193
194
195 progression = 0
196 pasProgression = 100.0 / (self.vNbrCarac.x * self.vNbrCarac.y)
197
198 self.vNbrCarac.x = int(self.vNbrCarac.x)
199 self.vNbrCarac.y = int(self.vNbrCarac.y)
200
201 self.tabImg = []
202
203 self.tabCouleur = []
204
205 self.tabCouleur256 = []
206
207 for ligne in range(self.vNbrCarac.y):
208 self.tabImg.append( [] )
209 self.tabCouleur.append( [] )
210 self.tabCouleur256.append( [] )
211 for colonne in range(self.vNbrCarac.x):
212
213 self.parent.jauge.SetValue( int(progression) )
214 progression += pasProgression
215
216 a = int(colonne*self.dimDamier.x)
217 b = int(ligne*self.dimDamier.y)
218 c = int(colonne*self.dimDamier.x+self.dimDamier.x)
219 d = int(ligne*self.dimDamier.y+self.dimDamier.y)
220 portion = self.img.crop( (a, b, c, d) )
221 por = portion.load()
222
223 portion256 = self.img256.crop( (a, b, c, d) )
224 por256 = portion256.load()
225
226 poids = 0.00001
227 poidsC = {'r': 0, 'v': 0, 'b': 0}
228 poidsC256 = {'r': 0, 'v': 0, 'b': 0}
229
230 for y in range(portion.size[1]):
231 for x in range(portion.size[0]):
232 pix = por[x, y]
233 pix256 = tableCouleur[por256[x, y]]
234 p = ( pix[0]/255.0 + pix[1]/255.0 + pix[2]/255.0 ) / 3.0
235 poids += p
236
237 poidsC = {'r': poidsC['r']+pix[0], 'v': poidsC['v']+pix[1], 'b': poidsC['b']+pix[2] }
238 poidsC256 = {'r': poidsC256['r']+pix256[0], 'v': poidsC256['v']+pix256[1], 'b': poidsC256['b']+pix256[2] }
239
240 nbrPixelMax = ((c-a) * (d-b))
241 poidsMoyen = float(poids) / nbrPixelMax
242 self.tabImg[ligne].append( poidsMoyen )
243
244 poidsMoyenC = {'r': poidsC['r'] / nbrPixelMax, 'v': poidsC['v'] / nbrPixelMax, 'b': poidsC['b'] / nbrPixelMax}
245 self.tabCouleur[ligne].append( poidsMoyenC )
246
247 poidsMoyenC256 = {'r': poidsC256['r'] / nbrPixelMax, 'v': poidsC256['v'] / nbrPixelMax, 'b': poidsC256['b'] / nbrPixelMax}
248 self.tabCouleur256[ligne].append( poidsMoyenC256 )
249
250
251 self.tabAscii = []
252 ligne = 0
253 for y in self.tabImg:
254 self.tabAscii.append( [] )
255 for x in y:
256 car = self.trouverCaractereProche ( self.poidsNBCarac, x, self.tolerancePoids )
257 self.tabAscii[ligne].append( car )
258
259 ligne += 1
260
261 txt = ''
262 for y in self.tabAscii:
263 for x in y:
264 txt += x
265 txt += '\n'
266
267 self.parent.jauge.SetValue( 100 )
268
269 return txt
270
271
273 """
274 Convertir un texte en rtf.
275
276 @param fichier: Nom et chemin de sauvegarde de l'image.
277 @param texte: Texte à convertir en image.
278 """
279
280 tailleTxt = self.parent.tailleCarac.GetValue() * 2
281
282 doc = Document()
283 ss = doc.StyleSheet
284
285 f = Font( 'monofur', 'monofur' )
286 ss.Fonts.append( f )
287 f = Font( 'Bitstream Vera Sans Mono', 'Bitstream Vera Sans Mono' )
288 ss.Fonts.append( f )
289 f = Font( 'White Rabbit', 'White Rabbit' )
290 ss.Fonts.append( f )
291 f = Font( 'Crystal', 'Crystal' )
292 ss.Fonts.append( f )
293 f = Font( 'ModeNine', 'ModeNine' )
294 ss.Fonts.append( f )
295
296 self.fontsRTF = {'Monofur': ss.Fonts.monofur, 'Bitstream Vera Sans Mono': ss.Fonts.BitstreamVeraSansMono, 'White Rabbit': ss.Fonts.WhiteRabbit,
297 'Courier New': ss.Fonts.CourierNew, 'Crystal': ss.Fonts.Crystal, 'Lucida Console': ss.Fonts.LucidaConsole, 'ModeNine': ss.Fonts.ModeNine }
298
299 section = Section()
300 p = Paragraph()
301
302 if self.parent.teinteCarac.GetSelection() == 0:
303
304 lignes = texte.split("\n")
305 for ligne in lignes:
306
307 p.append( TEXT( str(ligne.encode('latin-1') ), LINE, font=self.fontsRTF[self.police], size=tailleTxt ) )
308
309 else:
310 if self.parent.teinteCarac.GetSelection() == 1:
311 couleur = False
312 else:
313 couleur = True
314
315 nouvelleTable = []
316
317 self.indexMin = []
318
319 ii, jj = 0, 0
320 for j in self.tabCouleur256:
321 nouvelleTable.append( [] )
322 for i in j:
323
324 cCourante = Couleur(i['r'], i['v'], i['b'])
325
326 existante = False
327 index = 0
328 for c in self.indexMin:
329
330 if c == cCourante:
331 existante = True
332 break
333 index += 1
334
335 if not existante:
336 self.indexMin.append(cCourante)
337
338 nouvelleTable[jj].append( index )
339
340 ii += 1
341 jj += 1
342
343
344 tabCRTF = []
345 for y in nouvelleTable:
346 for x in y:
347 tabCRTF.append(x)
348 tabCRTF.append(None)
349
350 listeCol = []
351 i = 0
352 for s in range(len(self.indexMin)):
353 if couleur:
354 coul = Colour( str(s), self.indexMin[s].r, self.indexMin[s].v, self.indexMin[s].b)
355 else:
356 cg = int( (self.indexMin[s].r + self.indexMin[s].v + self.indexMin[s].b) / 3.0 )
357 coul = Colour( str(s), cg, cg, cg)
358 ss.Colours.append(coul)
359 listeCol.append(coul)
360 i += 1
361
362 i = 0
363 for c in texte:
364 if c != '\n':
365 p.append( TEXT( str(c.encode('latin-1') ), font=self.fontsRTF[self.police], size=tailleTxt, colour=listeCol[ tabCRTF[i] ] ) )
366 else:
367 p.append( LINE )
368 i += 1
369
370 section.append(p)
371 doc.Sections.append( section )
372
373
374 DR = Renderer()
375 DR.Write( doc, OpenFile( fichier[:-4] ) )
376
378 """
379 Convertir un texte en image.
380
381 @param fichier: Nom et chemin de sauvegarde de l'image.
382 @param texte: Texte à convertir en image.
383 @param format: Format de sauvegarde ('PNG', 'JPEG', etc.)
384 """
385
386 taillePolice = self.parent.tailleCarac.GetValue() * 2
387 texte = self.convertirImg_Txt(tailleCarac=taillePolice)
388
389
390 lignes = texte.split('\n')
391
392 font = ImageFont.truetype( self.parent.listeFonts[self.police], taillePolice)
393 tempo = Image.new('RGB', (20, 20), (255, 255, 255))
394 car = ImageDraw.Draw(tempo)
395 car.text((0, 0), lignes[0], font=font, fill=(0, 0, 0) )
396
397 dim = car.textsize(lignes[0], font=font)
398
399 dimX = dim[0]
400 dimY = dim[1] * len(lignes)
401
402 resultat = Image.new('RGB', (dimX, dimY), (255, 255, 255))
403
404 if self.parent.teinteCarac.GetSelection() == 0:
405
406 ligneX = dim[0]
407 ligneY = dim[1]
408 posY = 0
409 for ligne in lignes:
410
411 img = Image.new('RGB', (ligneX, ligneY), (255, 255, 255))
412 carac = ImageDraw.Draw(img)
413 carac.text((0, 0), ligne, font=font, fill=(0, 0, 0) )
414
415 resultat.paste( img, (0, posY) )
416
417 posY += ligneY
418 else:
419
420 if self.parent.teinteCarac.GetSelection() == 1:
421 couleur = False
422 else:
423 couleur = True
424
425 tabC = []
426 for y in self.tabCouleur:
427 for x in y:
428 tabC.append(x)
429
430 font = ImageFont.truetype( self.parent.listeFonts[self.police], taillePolice)
431 tempo = Image.new('RGB', (20, 20), (255, 255, 255))
432 car = ImageDraw.Draw(tempo)
433 car.text((0, 0), lignes[0][0], font=font, fill=(0, 0, 0) )
434
435 dimCar = car.textsize(lignes[0][0], font=font)
436
437
438 ligneX = dim[0]
439 ligneY = dim[1]
440 posX = 0
441 posY = 0
442 i = 0
443 for ligne in lignes:
444 img = Image.new('RGB', (dimCar[0], dimCar[1]), (255, 255, 255))
445
446 for c in ligne:
447
448 img = Image.new('RGB', (dimCar[0], dimCar[1]), (255, 255, 255))
449 carac = ImageDraw.Draw(img)
450 if couleur:
451 carac.text((0, 0), c, font=font, fill=(tabC[i]['r'], tabC[i]['v'], tabC[i]['b']) )
452 else:
453 cg = int( (tabC[i]['r'] + tabC[i]['v'] + tabC[i]['b']) / 3.0 )
454 carac.text((0, 0), c, font=font, fill=(cg, cg, cg) )
455
456 resultat.paste( img, (posX, posY) )
457 posX += dimCar[0]
458
459 i += 1
460
461 posY += dimCar[1]
462 posX = 0
463
464
465
466 resultat.save( fichier, format)
467
469 """
470 Cherche le caractère le plus proche d'un poids donné.
471
472 @param table: Dictionnaire des caractères et de leurs poids respectifs {'a': 0.456, 'b': 0.563, ... }
473 @param poids: Poids dont on cherche la correspondance avec la table.
474 @param tolerance: Différence de poids acceptable entre le poids cherché et le poids testé.
475 @return: Le caractère le plus proche du poids entré en paramètre.
476 """
477 for t in table:
478 if poids <= (table[t]+tolerance) and poids >= (table[t]-tolerance):
479 return t
480
481 return self.caractereDefaut
482