randstrip.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #!/usr/bin/env python3
  2. from PIL import Image
  3. from PIL import ImageFont
  4. from PIL import ImageDraw
  5. import csv
  6. import random
  7. import os
  8. import json
  9. fileDir = os.path.dirname(os.path.abspath(__file__))
  10. def replaceText(text,config):
  11. """This function replace $WILDCARD with a word found in subs.csv
  12. subs.csv definition is 1st colum $WILDCARD, subsequent columns, possible values (chosen at random), delimiter is ;"""
  13. with open(config["csvLocation"]+"/"+config["csvSubs"]) as subs:
  14. csvReader = csv.reader(subs,delimiter=";")
  15. for row in csvReader:
  16. if text.find(row[0]) != -1:
  17. text = text.replace(row[0],row[random.randint(1,len(row)-1)],1)
  18. return text
  19. def fetchText(indText,config):
  20. """This function fetch the text for the image with two characters
  21. rtext.csv definition is: 1st column the name of the file (i.e. B001.png), 2nd number of actors (at the moment
  22. they are limited to two; then a couple of columns or each actor with x and y coord of the strings; after the coords the outcomes,
  23. one column for each actor
  24. Delimiter is ; and line feeds @, if there aren't any options, it returns 0 (no text)
  25. It returns two arrays, coords is a tuple (x,y) and result is the outcome"""
  26. with open(config["csvLocation"]+"/"+config["csvSpeech"]) as rtext:
  27. csvReader = csv.reader(rtext,delimiter=';')
  28. for row in csvReader:
  29. if row[0]==indText:
  30. noActors = int(row[1])
  31. if noActors == 0:
  32. return 0
  33. else:
  34. firstElement = 2+(noActors*2)
  35. lastElement = len(row)-(noActors-1)
  36. randQuote = random.randrange(firstElement,lastElement,noActors)
  37. coords = []
  38. result = []
  39. for x in range(0,noActors):
  40. coords.append((row[2+x*2],row[3+x*2]))
  41. result.append(row[randQuote+x])
  42. return coords,result
  43. def fetchVign(config):
  44. """This functions fetch an image, randomly, chosen from a markov tree defined in ram.csv
  45. ram.csv definition is: 1st column the name of the image (without extension), subsequent columns, possible outcomes chosen randomly
  46. It returns an array with the file names"""
  47. starts = []
  48. startdest = []
  49. nvign = 0
  50. currVign = "000"
  51. story = []
  52. with open(config["csvLocation"]+"/"+config["csvTree"]) as ram:
  53. csvReader = csv.reader(ram)
  54. for row in csvReader:
  55. starts.append(row[0])
  56. startdest.append(row)
  57. while nvign <100:
  58. story.append(startdest[starts.index(currVign)][random.randint(1,len(startdest[starts.index(currVign)])-1)])
  59. currVign = story[nvign]
  60. if currVign == "END":
  61. return story
  62. story[nvign]+=".png"
  63. nvign +=1
  64. print("tree with no END")
  65. quit()
  66. def addThing(indVign,config):
  67. """This function adds a small image (object) to a larger image
  68. obj.csv definition is: name of the image (i.e. A001.png), x-coord, y-coord, subsequent columns possible outcomes
  69. It returns a tuple (object file name, x, y)"""
  70. with open(config["csvLocation"]+"/"+config["csvObj"]) as obj:
  71. csvReader = csv.reader(obj)
  72. for row in csvReader:
  73. if row[0] == indVign:
  74. return row[random.randint(3,len(row)-1)],row[1],row[2]
  75. return 0
  76. def writeStrip(story,fontSize,config):
  77. """This function creates the strip returning an image object that could be saved or viewed. It takes an array with filenames as parameter
  78. The first image is always 000, then appends to strip the files, then decorates it fetching text and adding objects. If the object is an R, then
  79. repeats the last object."""
  80. strip = []
  81. for indVign in story:
  82. try:
  83. vign = Image.open(config["imagesLocation"]+"/"+indVign).convert('RGBA')
  84. addtext = ImageDraw.Draw(vign)
  85. fnt = ImageFont.truetype(config["font"],fontSize)
  86. textVign = fetchText(indVign,config)
  87. if textVign!=0:
  88. try:
  89. for x in range(len(textVign[0])):
  90. text_vign = textVign[1][x]
  91. try:
  92. while text_vign.find('$') != -1:
  93. text_vign = replaceText(text_vign,config)
  94. except AttributeError:
  95. print("Problem parsing:")
  96. print(textVign)
  97. quit()
  98. text_vign = text_vign.replace('@','\n')
  99. addtext.multiline_text((int(textVign[0][x][0]),int(textVign[0][x][1])),text_vign,fill="#000000",font=fnt,align="center")
  100. except TypeError:
  101. print("Problem finding text for:")
  102. print(indVign)
  103. quit()
  104. obj = addThing(indVign,config)
  105. if obj!=0:
  106. if obj[0] == 'R':
  107. objImg = Image.open(config["imagesLocation"]+"/"+prevObj[0])
  108. else:
  109. prevObj = obj
  110. objImg = Image.open(config["imagesLocation"]+"/"+obj[0])
  111. vign.paste(objImg,(int(obj[1]),int(obj[2])))
  112. strip.append(vign)
  113. except FileNotFoundError:
  114. pass
  115. image = Image.new('RGBA',(config["xSize"],config["ySize"]))
  116. xshift=0
  117. for vign in strip:
  118. image.paste(vign,(xshift,0))
  119. xshift += config["panelLength"]
  120. return image
  121. def createStrip(config,specialPlatform="",fontSize=22):
  122. """Create strip and save it
  123. createStrip(str path/filename)"""
  124. try:
  125. story = fetchVign(config)
  126. finalStrip = writeStrip(story,fontSize,config)
  127. if specialPlatform == "android":
  128. return finalStrip
  129. else:
  130. finalStrip.save(config["saveLocation"]+config["filename"])
  131. return 0
  132. except Exception as err:
  133. return err
  134. def readConfig(profile=False,platform=False):
  135. """Read configuration file"""
  136. try:
  137. with open(fileDir+"/config.json") as f:
  138. config = json.load(f)
  139. except IOError:
  140. print("config.json not found")
  141. return False
  142. if not(profile):
  143. profile = config["defaultProfile"]
  144. else:
  145. profile = profile[0]
  146. try:
  147. checkProfile = config[profile]
  148. except KeyError:
  149. print("Profile "+profile+" not found")
  150. quit()
  151. saveLocation = checkLocal(config[profile]["saveLocation"])
  152. imagesLocation = checkLocal(config[profile]["imagesLocation"])
  153. csvLocation = checkLocal(config[profile]["csvLocation"])
  154. csvTree = config[profile]["csvTree"]
  155. csvSpeech = config[profile]["csvSpeech"]
  156. csvSubs = config[profile]["csvSubs"]
  157. csvObj = config[profile]["csvObj"]
  158. font = checkLocal(config[profile]["font"])
  159. xSize = config[profile]["xSize"]
  160. ySize = config[profile]["ySize"]
  161. panelLength = config[profile]["panelLength"]
  162. if platform:
  163. token = checkLocal(config[profile][platform]["token"])
  164. filename = checkLocal(config[profile][platform]["filename"])
  165. return {"saveLocation":saveLocation,"imagesLocation":imagesLocation,"csvLocation":csvLocation,"font":font,"token":token,"filename":filename,"xSize":xSize,"ySize":ySize,"panelLength":panelLength,"csvTree":csvTree,"csvSpeech":csvSpeech,"csvSubs":csvSubs,"csvObj":csvObj}
  166. filename = config[profile]["filename"]
  167. return {"saveLocation":saveLocation,"imagesLocation":imagesLocation,"csvLocation":csvLocation,"font":font,"filename":filename,"xSize":xSize,"ySize":ySize,"panelLength":panelLength,"csvTree":csvTree,"csvSpeech":csvSpeech,"csvSubs":csvSubs,"csvObj":csvObj}
  168. def checkLocal(directory):
  169. """Checks if it's a relative or absolute path"""
  170. if directory[0] == ".":
  171. return fileDir + directory[1:]
  172. else:
  173. return directory
  174. if __name__ == "__main__":
  175. import argparse
  176. parser = argparse.ArgumentParser()
  177. parser.add_argument('-s','--story',metavar='story',default='',nargs=4,help='name of the images')
  178. parser.add_argument('-m','--multiple',metavar='multiple',default=[1],nargs=1,type=int,help='multiple output (int >0)')
  179. parser.add_argument('-x','--xsize',metavar='xsize',default=0,type=int,nargs=1,help='resize image x')
  180. parser.add_argument('-p','--profile',metavar='profile',default="",type=str,nargs=1,help='profile')
  181. parser.add_argument('-o','--output',metavar='output',const=True,default=False,nargs="?",help='output file, if name not specified, default path will be used')
  182. args = parser.parse_args()
  183. if args.multiple[0] <= 0: #Wrong multiple choice
  184. quit()
  185. config = readConfig(profile=args.profile)
  186. if args.output == True: #Output on but no filename specified
  187. fileName = config["saveLocation"]+config["filename"]
  188. elif type(args.output) == str: #Output specified
  189. fileName = args.output
  190. for ist in range(0,args.multiple[0]):
  191. if (args.story == ''): #No story specified
  192. story = fetchVign(config)
  193. else:
  194. story = [] #Story specified
  195. for x in args.story:
  196. story.append(x)
  197. finalStrip = writeStrip(story,22,config)
  198. if args.xsize != 0: #Resize specified
  199. finalStrip = finalStrip.resize((args.xsize[0],int(args.xsize[0]/2400*500)))
  200. if args.multiple[0] == 1: #No multiple selected
  201. if args.output == False:
  202. finalStrip.show()
  203. else:
  204. finalStrip.save(fileName)
  205. else: #Multiple selected
  206. if args.output == False:
  207. print(story)
  208. else:
  209. finalStrip.save(str(ist)+fileName+".png")