This repository has been archived on 2020-09-21. You can view files and clone it, but cannot push or open issues or pull requests.
SotMDeckBuilder/SotMDeckBuilder.py
Adam Goldsmith edb8e40988 Simplify card sheet generation by keeping files as SVG longer
rather than using temporary png files and combining them together
later, merge files together in SVG and then just render once
2017-08-03 19:05:50 -04:00

212 lines
7.6 KiB
Python
Executable File

#!/usr/bin/env python3
import json
import math
import os
import subprocess
import sys
from lxml import etree
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
CARD_HEIGHT = 253
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.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):
for e in svg.findall('{http://www.w3.org/2000/svg}g'):
e.set("transform", e.get("transform", "") + " translate(" + \
str((cardNum % baseX) * CARD_WIDTH) + " " + \
str(int(cardNum / baseX) * CARD_HEIGHT) + ")")
baseImg.getroot().append(svg.getroot())
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 = etree.ElementTree(
etree.Element('svg',
attrib={'width': str(baseX * CARD_WIDTH) + "pt",
'height': str(baseX * CARD_HEIGHT) + "pt",
'version': "1.2",
'xmlns': "http://www.w3.org/2000/svg"}))
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.write(outfile + ".svg")
subprocess.call(["inkscape", "-z", "-f", outfile + ".svg", "-w",
str(baseX * CARD_WIDTH * 4), "-e", outfile + ".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.get('count', 1)):
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)