#!/usr/bin/env python3 import json import math import os import subprocess import sys from lxml import etree from PIL import Image if getattr(sys, 'frozen', False): # we are running in a bundle bundle_dir = sys._MEIPASS else: # we are running in a normal Python environment bundle_dir = os.path.dirname(os.path.abspath(__file__)) CARD_WIDTH = 181 * 4 CARD_HEIGHT = 253 * 4 def setText(tree, id, text): element = tree.find('.//*[@id="' + id + '"]') if element is None: print("id", id, "not found") return elif element.tag == "{http://www.w3.org/2000/svg}flowRoot": for e in element.findall("{http://www.w3.org/2000/svg}flowPara"): element.remove(e) # clear child paragraphs lines = str(text).splitlines() for line in lines: etree.SubElement(element, "{http://www.w3.org/2000/svg}flowPara").text=line else: element[0].text = str(text) def removeElement(tree, id): mark = tree.find('.//*[@id="' + id + '"]') if mark is not None: mark.getparent().remove(mark) def makeSVG(base, properties): tree = etree.parse(base) for id, text in properties.items(): if id not in ["count", "art"]: setText(tree, id, text) # remove HP mark if there is no hp if "hp" not in properties: removeElement(tree, "hpMark") # remove setup box if there is no setup if "setup" not in properties: removeElement(tree, "setupBox") if "art" in properties: art = tree.find('.//*[@id="art"]') if art is not None: art.set("{http://www.w3.org/1999/xlink}href", "file://" + bundle_dir + "/" + properties["art"]) return tree def addCardToBase(svg, baseImg, baseX, cardNum): # TODO: possible to remove all this writing to temp files? dest = "out/fig" + str(cardNum) svg.write(dest + ".svg") subprocess.call(["inkscape", "-z", "-f", dest + ".svg", "-w", str(CARD_WIDTH), "-e", dest + ".png"]) cardImg = Image.open(dest + ".png") baseImg.paste(cardImg, ((cardNum % baseX) * cardImg.width, int(cardNum / baseX) * cardImg.height)) def makeFace(baseImage, baseX, cardNum, base, card): fig = makeSVG(base, card) addCardToBase(fig, baseImage, baseX, cardNum) def makeFaces(deckJson, outfile): baseX = math.ceil(math.sqrt(len(deckJson['deck']) + len(deckJson['character']) * 2)) baseImage = Image.new('RGB', (CARD_WIDTH * baseX, CARD_HEIGHT * baseX)) cardType = deckJson["type"] cardNum = 0 # Make a card for each hero character card if cardType == "hero": for card in deckJson['character']: makeFace(baseImage, baseX, cardNum, os.path.join("images", cardType, "charFront.svg"), card) cardNum += 1 makeFace(baseImage, baseX, cardNum, os.path.join("images", cardType, "charBack.svg"), card) cardNum += 1 # Make a character and instructions card for each villain card elif cardType == "villain": for card in deckJson['character']: front = card["front"] front["name"] = card["name"] back = card["back"] back["name"] = card["name"] makeFace(baseImage, baseX, cardNum, os.path.join("images", cardType, "character.svg"), front) cardNum += 1 makeFace(baseImage, baseX, cardNum, os.path.join("images", cardType, "character.svg"), back) cardNum += 1 makeFace(baseImage, baseX, cardNum, os.path.join("images", cardType, "instructions.svg"), front) cardNum += 1 makeFace(baseImage, baseX, cardNum, os.path.join("images", cardType, "instructions.svg"), back) cardNum += 1 # Make a card for each card for card in deckJson['deck']: makeFace(baseImage, baseX, cardNum, os.path.join("images", cardType, "card.svg"), card) cardNum += 1 baseImage.save(outfile + ".png", "PNG") return baseX def makeCardJson(template, nickname, description, cardID): card = template.copy() card.update({"Nickname": nickname, "Description": description, "CardID": cardID}) return card def makeDoubleSidedCardJson(template, nickname, descriptionFront, descriptionBack, cardID): cardBack = makeCardJson(template, nickname, descriptionBack, cardID + 1) card = template.copy() card.update({"Nickname": nickname, "Description": descriptionFront, "CardID": cardID, "States": {"2": cardBack}}) return card def makeJson(deckJson, imgWidth, outfile): with open(bundle_dir + "/templates/deck.json") as f: outJson = json.load(f) with open(bundle_dir + "/templates/card.json") as f: cardTemplate = json.load(f) # number of cards in x and y direction outJson['ObjectStates'][0]['CustomDeck']['1'].update( {"NumWidth": imgWidth, "NumHeight": imgWidth, #"FaceURL": "http://adam-desktop.dyn.wpi.edu:8000/" + outfile + ".png", "FaceURL": "file://" + os.getcwd() + "/" + outfile + ".png", "BackURL": "http://cloud-3.steamusercontent.com/ugc/156906385556221451/CE2C3AFE1759790CB0B532FFD636D05A99EC91F4/"}) # decks start at (10 * deck id) cardNum = 100 if deckJson["type"] == "hero": for card in deckJson['character']: outJson['ObjectStates'][0]['DeckIDs'].append(cardNum) cardOut = makeDoubleSidedCardJson( cardTemplate, card['name'], "Active", "Incapacitated", cardNum) outJson['ObjectStates'][0]['ContainedObjects'].append(cardOut) cardNum += 2 elif deckJson["type"] == "villain": for card in deckJson['character']: outJson['ObjectStates'][0]['DeckIDs'].append(cardNum) cardOut = makeDoubleSidedCardJson( cardTemplate, card['name'], "Front", "Back", cardNum) outJson['ObjectStates'][0]['ContainedObjects'].append(cardOut) cardNum += 2 outJson['ObjectStates'][0]['DeckIDs'].append(cardNum) cardOut = makeDoubleSidedCardJson( cardTemplate, card['name'] + " instructions", "Front", "Back", cardNum) outJson['ObjectStates'][0]['ContainedObjects'].append(cardOut) cardNum += 2 for card in deckJson['deck']: for i in range(0, card['count']): outJson['ObjectStates'][0]['DeckIDs'].append(cardNum) # add a card object thing for each card, and give it a name cardOut = makeCardJson(cardTemplate, card['name'], card.get('keywords', ""), cardNum) outJson['ObjectStates'][0]['ContainedObjects'].append(cardOut) cardNum += 1 with open(outfile + ".json", "w") as f: json.dump(outJson, f) if __name__ == '__main__': if len(sys.argv) < 3: print("not enough arguments!") inputJson = input("Input file: ") outfile = input("Output file (no suffix): ") else: inputJson = sys.argv[1] outfile = sys.argv[2] with open(inputJson) as f: deckJson = json.load(f) imgWidth = makeFaces(deckJson, outfile) makeJson(deckJson, imgWidth, outfile)