Card db refactoring (#105)

* Code refactoring for card db change (#86)
This assumes the following file structure:
```
card_db/
    cards_db.json
    sets_db.json
    <language>/
        cards_text.json
        sets_text.json
```
Files that are needed are:

- cards_db.json
- cards_text.json and sets_text.json for each supported language

**Other changes:**

Added option called `--edition` that can be "1", "2", "latest", or "all". Defaults to "all".
This allows for a quick filtering for those that don't want everything (i.e., "all").

- "1" is for all 1st editions of expansions. It also includes 2nd edition update packs.
- "2" is for all 2nd editions of expansions. So base, Dominion 2nd ed., and Intrigue 2nd ed. (no update packs, since already in 2nd edition)
- "latest" is for base, Dominion 2nd ed., and Intrigue 2nd ed., and all the remaining 1st editions.

Cards can be grouped 3 ways:

- No grouping (default)
- In expansion grouping invoked with `--special_card_groups`.
- Grouping across expansions with `--exclude_events` and `--exclude_landmarks`.  These groups are placed in a set called "Extras".   `--exclude_prizes` is not currently implemented.

Added an option called `--upgrade_with_expansion` which will put the upgraded cards into the corresponding earlier expansion.  So all 1st edition cards as well as the upgrade cards appear in the 1st edition set.  That way the cards are listed on the expansion dividers and any tab/ordering fit the expansion as a whole.  And because of the deleted cards in 2nd edition, this is a different list of cards than just using the 2nd edition.

* update set image mapping for 2nd edition icons
* add improved set icons from https://boardgamegeek.com/filepage/73273/dominion-card-icons-vector-images
* recompress all images
* new format for cards_db.json and translations
* Added short name to sets
* Updates to allow blank set images for base cards and fix blank set image for the "Extras" set.  Also removed base_set.png which is no longer needed (again!)
* scaled all set images to be exactly 300 x 300 pixels for consistency and better printing
* Updated __init__.py and cards.py to automatically find the "lowest cost" value from all the cards in a group.
* Update carddb_tests.py
  * Updated set information in testcases
  * Changed test of cardset to test cardset_tag instead.  Since that is what everything keys off of now.
  * Updated the language tests to pull in the language parts before making the check.
* Standardize on ISO8859-15 (#98)
* Remove Trash Card from 2nd edition, replace HTML line breaks & unicode
* Better Error Handling for Font Errors
* Added text formating codes
Added text formatting codes for "extra" and "description" fields.  This includes:
<tab> and <t> to add a tab (4 spaces)
<n> as an alternative to \n (hard new line)
<br> as an alternative to <br /> (soft new line)
<c> and <center> to center text in this paragraph until the next hard new line
<l> and <left> to left align text in this paragraph until the next hard new line
<r> and <right> to right align text in this paragraph until the next hard new line.
<line> to add a hard new line, a centered dividing line, and a trailing hard new line.
<line> to put a centered line
This goes with the <b>..</b> for bold, <i>..</i> for italics, and <u>..</u> for underline that was already existing (but probably not remembered.
* Update card count presentation (#116)
* wvoigt added automatic bonus highlighting (#119)
This commit is contained in:
Nick Vance authored and Peter committed 2016-12-10 21:05:34 -08:00
1 parent 3b732fa47f
commit 250a9e4b51
135 files changed
+20228 -14089

No files matched your search

+699 -340
View File
File diff suppressed because it is too large. Load diff
+142 -163
View File
@@ -1,74 +1,15 @@
import json
import os
import re
from reportlab.lib.units import cm
def getType(typespec):
return cardTypes[tuple(typespec)]
setImages = {
'dominion': 'base_set.png',
'intrigue': 'intrigue_set.png',
'seaside': 'seaside_set.png',
'prosperity': 'prosperity_set.png',
'alchemy': 'alchemy_set.png',
'cornucopia': 'cornucopia_set.png',
'cornucopia extras': 'cornucopia_set.png',
'hinterlands': 'hinterlands_set.png',
'dark ages': 'dark_ages_set.png',
'dark ages extras': 'dark_ages_set.png',
'guilds': 'guilds_set.png',
'adventures': 'adventures_set.png',
'adventures extras': 'adventures_set.png',
'empires': 'empires_set.png',
'empires extras': 'empires_set.png'
}
promoImages = {
'walled village': 'walled_village_set.png',
'stash': 'stash_set.png',
'governor': 'governor_set.png',
'black market': 'black_market_set.png',
'envoy': 'envoy_set.png',
'prince': 'prince_set.png',
'summon': 'promo_set.png',
'sauna': 'promo_set.png',
'avanto': 'promo_set.png',
'sauna \/ avanto': 'promo_set.png'
}
setTextIcons = {
'dominion': 'D',
'intrigue': 'I',
'seaside': 'S',
'prosperity': 'P',
'alchemy': 'A',
'cornucopia': 'C',
'cornucopia extras': 'C',
'hinterlands': 'H',
'dark ages': 'DA',
'dark ages extras': 'DA',
'guilds': 'G',
'adventures': 'Ad',
'adventures extras': 'Ad',
'empires': 'E',
'empires extras': 'E'
}
promoTextIcons = {
'walled village': '',
'stash': '',
'governor': '',
'black market': '',
'envoy': '',
'prince': ''
}
language_mapping = None
class Card(object):
language_mapping = None
sets = None
types = None
type_names = None
bonus_regex = None
class CardJSONEncoder(json.JSONEncoder):
@@ -81,56 +22,94 @@ class Card(object):
def decode_json(obj):
return Card(**obj)
@classmethod
def getSetImage(cls, setName, cardName):
if setName in setImages:
return setImages[setName]
if cardName.lower() in promoImages:
return promoImages[cardName.lower()]
if setName in cls.language_mapping:
trans = cls.language_mapping[setName]
if trans in setImages:
return setImages[trans]
if cardName in cls.language_mapping:
trans = cls.language_mapping[cardName]
if trans.lower() in promoImages:
return promoImages[trans.lower()]
return None
def __init__(self, name=None, cardset='', types=None, cost='', description='',
potcost=0, debtcost=0, extra='', count=-1, card_tag='missing card_tag',
cardset_tags=None, group_tag='', group_top=False, image=None,
text_icon=None, cardset_tag=''):
@classmethod
def getSetText(cls, setName, cardName):
if setName in setTextIcons:
return setTextIcons[setName]
if cardName.lower() in promoTextIcons:
return promoTextIcons[cardName.lower()]
return None
if types is None:
types = [] # make sure types is a list
if cardset_tags is None:
cardset_tags = [] # make sure cardset_tags is a list
if name is None:
name = card_tag # make sure there is a meaningful default name
def __init__(self, name, cardset, types, cost, description='', potcost=0, debtcost=0, extra='', count=-1):
self.name = name.strip()
self.cardset = cardset.strip()
self.types = types
self.types_name = ""
self.cost = cost
self.description = description
self.potcost = potcost
self.debtcost = debtcost
self.description = description
self.extra = extra
self.card_tag = card_tag
self.cardset_tags = cardset_tags
self.group_tag = group_tag
self.group_top = group_top
self.image = image
self.text_icon = text_icon
self.cardset_tag = cardset_tag
if count < 0:
self.count = getType(self.types).getTypeDefaultCardCount()
self.count = [self.getType().getTypeDefaultCardCount()]
elif count == 0:
self.count = []
else:
self.count = count
self.count = [int(count)]
def getCardCount(self):
return self.count
return sum(i for i in self.count)
def setCardCount(self, value):
self.count = value
def addCardCount(self, value):
self.count.extend(value)
def getStackHeight(self, thickness):
# return height of the stacked cards in cm. Using hight in cm of a stack of 60 Copper cards as thickness.
return self.count * cm * (thickness / 60.0) + 2
return self.getCardCount() * cm * (thickness / 60.0) + 2
def getType(self):
return getType(self.types)
return Card.types[tuple(self.types)]
def getBonusBoldText(self, text):
for regex in Card.bonus_regex:
text = re.sub(regex, '<b>\\1</b>', text)
return text
@staticmethod
def addBonusRegex(bonus):
# Each bonus_regex matches the bonus keywords to be highlighted
# This only needs to be done once per language
if Card.bonus_regex is None:
# initialize the information holder
Card.bonus_regex = []
# Make sure have minimum to to anything
if not isinstance(bonus, dict):
return
if 'include' not in bonus:
return
if not bonus['include']:
return
if 'exclude' not in bonus:
bonus['exclude'] = []
# Start processing of lists into a single regex statement
# (?i) makes this case insensitive
# (?!\<b\>) and (?!\<\/b\>) prevents matching already bolded items
# (?!\w) prevents smaller word matches. Prevents matching "Action" in "Actions"
if bonus['exclude']:
bonus['exclude'].sort(reverse=True)
exclude_regex = '(?!\w)(?!\s*(' + '|'.join(bonus['exclude']) + '))'
else:
exclude_regex = ''
bonus['include'].sort(reverse=True)
include_regex = "(\+\s*\d+\s*(" + '|'.join(bonus['include']) + "))"
regex = "((?i)(?!\<b\>)" + include_regex + exclude_regex + "(?!\<\/b\>))"
Card.bonus_regex.append(regex)
def __repr__(self):
return '"' + self.name + '"'
@@ -139,35 +118,79 @@ class Card(object):
return self.name + ' ' + self.cardset + ' ' + '-'.join(self.types)\
+ ' ' + self.cost + ' ' + self.description + ' ' + self.extra
def isExpansion(self):
return 'Expansion' in self.getType().getTypeNames()
def isEvent(self):
return 'Event' in self.getType().getTypeNames()
def isLandmark(self):
return 'Landmark' in self.getType().getTypeNames()
def isPrize(self):
return 'Prize' in self.getType().getTypeNames()
def isType(self, what):
return what in self.getType().getTypeNames()
def isExpansion(self):
return self.isType('Expansion')
def isEvent(self):
return self.isType('Event')
def isLandmark(self):
return self.isType('Landmark')
def isPrize(self):
return self.isType('Prize')
def get_total_cost(self, c):
# Return a tuple that represents the total cost of card c
# Hightest cost cards are in order:
# - Landmarks to sort at the very end
# - cards with * since that can mean anything
# - cards with numeric errors
# convert cost (a string) into a number
if c.isLandmark():
c_cost = 999
elif not c.cost:
c_cost = 0 # if no cost, treat as 0
elif '*' in c.cost:
c_cost = 998 # make it a really big number
else:
try:
c_cost = int(c.cost)
except ValueError:
c_cost = 997 # can't, so make it a really big number
return c_cost, c.potcost, c.debtcost
def set_lowest_cost(self, other):
# set self cost fields to the lower of the two's total cost
self_cost = self.get_total_cost(self)
other_cost = self.get_total_cost(other)
if other_cost < self_cost:
self.cost = other.cost
self.potcost = other.potcost
self.debtcost = other.debtcost
def setImage(self):
setImage = Card.getSetImage(self.cardset, self.name)
if setImage is None and self.cardset != 'base':
print 'warning, no set image for set "{}" card "{}"'.format(self.cardset, self.name)
setImage = None
if self.image is not None:
setImage = self.image
else:
if self.cardset_tag in Card.sets:
if 'image' in Card.sets[self.cardset_tag]:
setImage = Card.sets[self.cardset_tag]['image']
if setImage is None and self.cardset_tag != 'base':
print 'warning, no set image for set "{}", card "{}"'.format(self.cardset, self.name)
return setImage
def setTextIcon(self):
setTextIcon = Card.getSetText(self.cardset, self.name)
setTextIcon = None
if self.text_icon:
setTextIcon = self.text_icon
else:
if self.cardset_tag in Card.sets:
if 'text_icon' in Card.sets[self.cardset_tag]:
setTextIcon = Card.sets[self.cardset_tag]['text_icon']
if setTextIcon is None and self.cardset != 'base':
print 'warning, no set text for set "{}" card "{}"'.format(self.cardset, self.name)
print 'warning, no set text for set "{}", card "{}"'.format(self.cardset, self.name)
return setTextIcon
def isBlank(self):
return False
return self.isType('Blank')
class BlankCard(Card):
@@ -181,12 +204,16 @@ class BlankCard(Card):
class CardType(object):
def __init__(self, typeNames, tabImageFile, defaultCardCount=10, tabTextHeightOffset=0, tabCostHeightOffset=-1):
self.typeNames = typeNames
self.tabImageFile = tabImageFile
@staticmethod
def decode_json(obj):
return CardType(**obj)
def __init__(self, card_type, card_type_image, defaultCardCount=10, tabTextHeightOffset=0, tabCostHeightOffset=-1):
self.typeNames = tuple(card_type)
self.tabImageFile = card_type_image
self.defaultCardCount = defaultCardCount
self.tabTextHeightOffset = tabTextHeightOffset
self.tabCostHeightOffset = tabCostHeightOffset
self.defaultCardCount = defaultCardCount
def getTypeDefaultCardCount(self):
return self.defaultCardCount
@@ -209,51 +236,3 @@ class CardType(object):
def getTabCostHeightOffset(self):
return self.tabCostHeightOffset
cardTypes = [
CardType(('Action',), 'action.png'),
CardType(('Action', 'Attack'), 'action.png'),
CardType(('Action', 'Attack', 'Prize'), 'action.png', 1),
CardType(('Action', 'Reaction'), 'reaction.png'),
CardType(('Action', 'Victory'), 'action-victory.png', 12),
CardType(('Action', 'Duration'), 'duration.png', 10),
CardType(('Action', 'Duration', 'Reaction'), 'duration-reaction.png'),
CardType(('Action', 'Attack', 'Duration'), 'duration.png'),
CardType(('Action', 'Looter'), 'action.png'),
CardType(('Action', 'Prize'), 'action.png', 1),
CardType(('Action', 'Ruins'), 'ruins.png', 10, 0, 1),
CardType(('Action', 'Shelter'), 'action-shelter.png', 6),
CardType(('Action', 'Attack', 'Duration'), 'duration.png'),
CardType(('Action', 'Attack', 'Looter'), 'action.png'),
CardType(('Action', 'Attack', 'Traveller'), 'action.png', 5),
CardType(('Action', 'Reserve'), 'reserve.png', 10),
CardType(('Action', 'Reserve', 'Victory'), 'reserve-victory.png', 12),
CardType(('Action', 'Traveller'), 'action.png', 5),
CardType(('Action', 'Gathering'), 'action.png'),
CardType(('Action', 'Treasure'), 'action-treasure.png'),
CardType(('Prize',), 'action.png', 1),
CardType(('Event',), 'event.png', 1),
CardType(('Reaction',), 'reaction.png'),
CardType(('Reaction', 'Shelter'), 'reaction-shelter.png', 6),
CardType(('Treasure',), 'treasure.png', 10, 3, 0),
CardType(('Treasure', 'Attack'), 'treasure.png'),
CardType(('Treasure', 'Victory'), 'treasure-victory.png', 12),
CardType(('Treasure', 'Prize'), 'treasure.png', 1, 3, 0),
CardType(('Treasure', 'Reaction'), 'treasure-reaction.png', 10, 0, 1),
CardType(('Treasure', 'Reserve'), 'reserve-treasure.png'),
CardType(('Victory',), 'victory.png', 12),
CardType(('Victory', 'Reaction'), 'victory-reaction.png', 12, 0, 1),
CardType(('Victory', 'Shelter'), 'victory-shelter.png', 6),
CardType(('Victory', 'Castle'), 'victory.png', 12),
CardType(('Curse',), 'curse.png', 30, 3),
CardType(('Trash',), 'action.png', 1),
CardType(('Prizes',), 'action.png', 0),
CardType(('Events',), 'event.png', 0),
CardType(('Shelters',), 'shelter.png', 0),
CardType(('Expansion',), 'expansion.png', 0, 4),
CardType(('Blank',), ''),
CardType(('Landmark',), 'landmark.png', 1),
CardType(('Landmarks',), 'landmark.png', 0)
]
cardTypes = dict(((c.getTypeNames(), c) for c in cardTypes))
+196 -79
View File
@@ -6,9 +6,10 @@ from reportlab.lib.units import cm
from reportlab.pdfbase import pdfmetrics
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER
from reportlab.pdfgen import canvas
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase.pdfmetrics import stringWidth
def split(l, n):
@@ -25,19 +26,21 @@ class DividerDrawer(object):
self.canvas = None
def registerFonts(self):
try:
dirn = os.path.join(self.options.data_path, 'fonts')
dirn = os.path.join(self.options.data_path, 'fonts')
regularMpath = os.path.join(dirn, 'MinionPro-Regular.ttf')
boldMpath = os.path.join(dirn, 'MinionPro-Bold.ttf')
obliqueMpath = os.path.join(dirn, 'MinionPro-It.ttf')
if os.path.isfile(regularMpath) and os.path.isfile(boldMpath) and os.path.isfile(obliqueMpath):
self.fontNameRegular = 'MinionPro-Regular'
pdfmetrics.registerFont(TTFont(self.fontNameRegular, os.path.join(
dirn, 'MinionPro-Regular.ttf')))
pdfmetrics.registerFont(TTFont(self.fontNameRegular, regularMpath))
self.fontNameBold = 'MinionPro-Bold'
pdfmetrics.registerFont(TTFont(self.fontNameBold, os.path.join(
dirn, 'MinionPro-Bold.ttf')))
pdfmetrics.registerFont(TTFont(self.fontNameBold, boldMpath))
self.fontNameOblique = 'MinionPro-Oblique'
pdfmetrics.registerFont(TTFont(self.fontNameOblique, os.path.join(
dirn, 'MinionPro-It.ttf')))
except:
print >> sys.stderr, "Warning, Minion Pro Font ttf file not found! Falling back on Times"
pdfmetrics.registerFont(TTFont(self.fontNameOblique, obliqueMpath))
else:
print >> sys.stderr, "Warning, Minion Pro Font ttf file(s) not found! Falling back on Times. Tried:"
print >> sys.stderr, regularMpath + ' ' + boldMpath + ' & ' + obliqueMpath
self.fontNameRegular = 'Times-Roman'
self.fontNameBold = 'Times-Bold'
self.fontNameOblique = 'Times-Oblique'
@@ -205,7 +208,11 @@ class DividerDrawer(object):
self.canvas.save()
def add_inline_images(self, text, fontsize):
# Coins
path = os.path.join(self.options.data_path, 'images')
replace = '<img src=' "'%s/coin_small_\\1.png'" ' width=%d height=' "'200%%'" ' valign=' "'middle'" '/>'
replace = replace % (path, fontsize * 2.4)
text = re.sub('(\d+)\s*\<\*COIN\*\>', replace, text)
replace = '<img src=' "'%s/coin_small_\\1.png'" ' width=%d height=' "'100%%'" ' valign=' "'middle'" '/>'
replace = replace % (path, fontsize * 1.2)
text = re.sub('(\d+)\s(c|C)oin(s)?', replace, text)
@@ -215,19 +222,66 @@ class DividerDrawer(object):
replace = '<img src=' "'%s/coin_small_empty.png'" ' width=%d height=' "'100%%'" ' valign=' "'middle'" '/>'
replace = replace % (path, fontsize * 1.2)
text = re.sub('empty\s(c|C)oin(s)?', replace, text)
text = re.sub('\_\s(c|C)oin(s)?', replace, text)
# VP
replace = '<img src=' "'%s/victory_emblem.png'" ' width=%d height=' "'120%%'" ' valign=' "'middle'" '/>'
replace = replace % (path, fontsize * 1.5)
text = re.sub('\<VP\>', replace, text)
text = re.sub('(?:\s+|\<)VP(?:\s+|\>|\.|$)', replace, text)
replace = '<font size=%d>\\1</font> '
replace += '<img src=' "'%s/victory_emblem.png'" ' width=%d height=' "'200%%'" ' valign=' "'middle'" '/>'
replace = replace % (fontsize * 1.5, path, fontsize * 2.5)
text = re.sub('(\d+)\s*\<\*VP\*\>', replace, text)
# Debt
replace = '<img src=' "'%s/debt_\\1.png'" ' width=%d height=' "'105%%'" ' valign=' "'middle'" '/>&thinsp;'
replace = replace % (path, fontsize * 1.2)
text = re.sub('(\d+)\sDebt', replace, text)
replace = '<img src=' "'%s/debt.png'" ' width=%d height=' "'105%%'" ' valign=' "'middle'" '/>&thinsp;'
replace = replace % (path, fontsize * 1.2)
text = re.sub('Debt', replace, text)
# Potion
replace = '<font size=%d>\\1</font> '
replace += '<img src=' "'%s/potion_small.png'" ' width=%d height=' "'140%%'" ' valign=' "'middle'" '/>'
replace = replace % (fontsize * 1.5, path, fontsize * 2.0)
text = re.sub('(\d+)\s*\<\*POTION\*\>', replace, text)
replace = '<img src=' "'%s/potion_small.png'" ' width=%d height=' "'100%%'" ' valign=' "'middle'" '/>'
replace = replace % (path, fontsize * 1.2)
text = re.sub('Potion', replace, text)
return text
return text.strip()
def add_inline_text(self, card, text):
# Bonuses
text = card.getBonusBoldText(text)
# <line>
replace = "<center>%s\n" % ("&ndash;" * 22)
text = re.sub("\<line\>", replace, text)
# <tab> and \t
text = re.sub("\<tab\>", '\t', text)
text = re.sub("\<t\>", '\t', text)
text = re.sub("\t", "&nbsp;" * 4, text)
# various breaks
text = re.sub("\<br\>", "<br />", text)
text = re.sub("\<n\>", "\n", text)
# alignments
text = re.sub("\<c\>", "<center>", text)
text = re.sub("\<center\>", "\n<para alignment='center'>", text)
text = re.sub("\<l\>", "<left>", text)
text = re.sub("\<left\>", "\n<para alignment='left'>", text)
text = re.sub("\<r\>", "<right>", text)
text = re.sub("\<right\>", "\n<para alignment='right'>", text)
text = re.sub("\<j\>", "<justify>", text)
text = re.sub("\<justify\>", "\n<para alignment='justify'>", text)
return text.strip().strip('\n')
def drawOutline(self,
card,
@@ -302,38 +356,72 @@ class DividerDrawer(object):
self.canvas.restoreState()
def drawCardCount(self, card, x, y, offset=-1):
if card.count < 1:
# Note that this is right justified.
# x represents the right most for the image (image grows to the left)
if card.getCardCount() < 1:
return 0
# base width is 16 (for image) + 2 (1 pt border on each side)
width = 18
# draw_list = [(card.getCardCount(), 1)]
draw_list = sorted([(i, card.count.count(i)) for i in set(card.count)])
cardIconHeight = y + offset
countHeight = cardIconHeight - 4
width = 0
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images', 'card.png'),
x,
countHeight,
16,
16,
preserveAspectRatio=True,
mask='auto')
for value, count in draw_list:
# draw the image set with the number of cards inside it
width += 16
x -= 16
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images', 'card.png'),
x,
countHeight,
16,
16,
preserveAspectRatio=True,
mask='auto')
self.canvas.setFont(self.fontNameBold, 10)
self.canvas.drawCentredString(x + 8, countHeight + 4, str(value))
self.canvas.setFont(self.fontNameBold, 10)
count = str(card.count)
self.canvas.drawCentredString(x + 8, countHeight + 4, count)
return width
# now draw the number of sets
if count > 1:
count_string = u"{}\u00d7".format(count)
width_string = stringWidth(count_string, self.fontNameRegular, 10)
width_string -= 1 # adjust to make it closer to image
width += width_string
x -= width_string
self.canvas.setFont(self.fontNameRegular, 10)
self.canvas.drawString(x, countHeight + 4, count_string)
return width + 1
def drawCost(self, card, x, y, costOffset=-1):
# base width is 16 (for image) + 2 (1 pt border on each side)
width = 18
# width starts at 2 (1 pt border on each side)
width = 2
costHeight = y + costOffset
coinHeight = costHeight - 5
potHeight = y - 3
potSize = 11
if (not(card.cost == "" or
(card.debtcost and int(card.cost) == 0) or
(card.potcost and int(card.cost) == 0))):
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images', 'coin_small.png'),
x,
coinHeight,
16,
16,
preserveAspectRatio=True,
mask='auto')
self.canvas.setFont(self.fontNameBold, 12)
self.canvas.drawCentredString(x + 8, costHeight, str(card.cost))
self.canvas.setFillColorRGB(0, 0, 0)
x += 17
width += 16
if card.debtcost:
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images', 'debt.png'),
@@ -342,53 +430,30 @@ class DividerDrawer(object):
16,
16,
preserveAspectRatio=True,
mask=[255, 255, 255, 255, 255, 255])
cost = str(card.debtcost)
if card.cost != "" and int(card.cost) > 0:
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images',
'coin_small.png'),
x + 17,
coinHeight,
16,
16,
preserveAspectRatio=True,
mask=[255, 255, 255, 255, 255, 255])
self.canvas.setFont(self.fontNameBold, 12)
self.canvas.drawCentredString(x + 8 + 17, costHeight,
str(card.cost))
self.canvas.setFillColorRGB(0, 0, 0)
width += 16
mask=[170, 255, 170, 255, 170, 255])
self.canvas.setFillColorRGB(1, 1, 1)
else:
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images',
'coin_small.png'),
x,
coinHeight,
16,
16,
preserveAspectRatio=True,
mask='auto')
cost = str(card.cost)
self.canvas.setFont(self.fontNameBold, 12)
self.canvas.drawCentredString(x + 8, costHeight, str(card.debtcost))
self.canvas.setFillColorRGB(0, 0, 0)
x += 17
width += 16
if card.potcost:
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images', 'potion.png'),
x + 17,
x,
potHeight,
potSize,
potSize,
preserveAspectRatio=True,
mask=[255, 255, 255, 255, 255, 255])
mask='auto')
width += potSize
self.canvas.setFont(self.fontNameBold, 12)
self.canvas.drawCentredString(x + 8, costHeight, cost)
self.canvas.setFillColorRGB(0, 0, 0)
return width
def drawSetIcon(self, setImage, x, y):
# set image
w = 2
self.canvas.drawImage(
os.path.join(self.options.data_path, 'images', setImage),
x,
@@ -396,6 +461,7 @@ class DividerDrawer(object):
14,
12,
mask='auto')
return w + 14
def nameWidth(self, name, fontSize):
w = 0
@@ -509,7 +575,6 @@ class DividerDrawer(object):
width = self.nameWidth(name, fontSize)
while width > textWidth and fontSize > 8:
fontSize -= .01
# print 'decreasing font size for tab of',name,'now',fontSize
width = self.nameWidth(name, fontSize)
tooLong = width > textWidth
if tooLong:
@@ -539,7 +604,7 @@ class DividerDrawer(object):
if wrapper == "back" and not self.options.tab_name_align == "centre":
NotRightEdge = not NotRightEdge
if NotRightEdge:
if self.options.tab_name_align == "centre":
if self.options.tab_name_align == "centre" or self.wantCentreTab(card):
w = self.options.labelWidth / 2 - self.nameWidth(
line, fontSize) / 2
else:
@@ -558,11 +623,18 @@ class DividerDrawer(object):
w += drawWordPiece(word[0], fontSize)
w += drawWordPiece(word[1:], fontSize - 2)
else:
# align text to the right if tab is on right side, to make
# tabs easier to read when grouped together extra 3pt is for
# align text to the right if tab is on right side
if self.options.tab_name_align == "centre" or self.wantCentreTab(card):
w = self.options.labelWidth / 2 - self.nameWidth(
line, fontSize) / 2
w = self.options.labelWidth - w
else:
w = self.options.labelWidth - textInsetRight
# to make tabs easier to read when grouped together extra 3pt is for
# space between text + set symbol
w -= 3
w = self.options.labelWidth - textInsetRight - 3
words.reverse()
def drawWordPiece(text, fontSize):
@@ -628,23 +700,59 @@ class DividerDrawer(object):
if self.options.notch_width1 > 0:
usedHeight += self.options.notch_height
# Add 'body-top' items
drewTopIcon = False
Image_x_left = 4
if 'body-top' in self.options.cost and not card.isExpansion():
self.drawCost(card, cm / 4.0, totalHeight - usedHeight - 0.5 * cm)
Image_x_left += self.drawCost(card, Image_x_left, totalHeight - usedHeight - 0.5 * cm)
drewTopIcon = True
Image_x = self.options.dividerWidth - 16
Image_x_right = self.options.dividerWidth - 4
if 'body-top' in self.options.set_icon and not card.isExpansion():
setImage = card.setImage()
if setImage:
self.drawSetIcon(setImage, Image_x,
Image_x_right -= 16
self.drawSetIcon(setImage, Image_x_right,
totalHeight - usedHeight - 0.5 * cm - 3)
Image_x -= 16
drewTopIcon = True
if self.options.count:
self.drawCardCount(card, Image_x,
totalHeight - usedHeight - 0.5 * cm)
Image_x_right -= self.drawCardCount(card, Image_x_right,
totalHeight - usedHeight - 0.5 * cm)
drewTopIcon = True
if (self.options.types and not card.isExpansion()):
# Calculate how much width have for printing
# Want centered, but number of other items can limit
left_margin = Image_x_left
right_margin = self.options.dividerWidth - Image_x_right
worst_margin = max(left_margin, right_margin)
w = self.options.dividerWidth / 2
textWidth = self.options.dividerWidth - 2 * worst_margin
textWidth2 = self.options.dividerWidth - left_margin - right_margin
# Calculate font size that will fit in the area
# Start with centering type. But if the fontSize gets too small
# use all the available space, even if it is not centered on the card
fontSize = 8
failover = False
width = stringWidth(card.types_name, self.fontNameRegular, fontSize)
while width > textWidth:
fontSize -= .01
if fontSize < 6 and not failover:
# Start over using all available space left on line
textWidth = textWidth2
w = left_margin + (textWidth2 / 2)
fontSize = 8
failover = True
width = stringWidth(card.types_name, self.fontNameRegular, fontSize)
# Print out the text in the right spot
h = totalHeight - usedHeight - 0.5 * cm
self.canvas.setFont(self.fontNameRegular, fontSize)
if card.types_name != ' ':
self.canvas.drawCentredString(w, h, card.types_name)
drewTopIcon = True
if drewTopIcon:
@@ -657,20 +765,23 @@ class DividerDrawer(object):
elif divider_text == "rules":
# Add the extra rules text to the divider
if card.extra:
descriptions = (card.extra, )
descriptions = card.extra
else:
# Asked for rules and they don't exist, so don't print anything
return
elif divider_text == "card":
# Add the card text to the divider
descriptions = re.split("\n", card.description)
descriptions = card.description
else:
# Don't know what was asked, so don't print anything
return
s = getSampleStyleSheet()['BodyText']
s.fontName = "Times-Roman"
s.alignment = TA_JUSTIFY
if divider_text == "card" and not card.isExpansion():
s.alignment = TA_CENTER
else:
s.alignment = TA_JUSTIFY
textHorizontalMargin = .5 * cm
textVerticalMargin = .3 * cm
@@ -679,12 +790,18 @@ class DividerDrawer(object):
spacerHeight = 0.2 * cm
minSpacerHeight = 0.05 * cm
if not card.isExpansion():
descriptions = self.add_inline_text(card, descriptions)
descriptions = re.split("\n", descriptions)
while True:
paragraphs = []
# this accounts for the spacers we insert between paragraphs
h = (len(descriptions) - 1) * spacerHeight
for d in descriptions:
dmod = self.add_inline_images(d, s.fontSize)
if card.isExpansion():
dmod = d
else:
dmod = self.add_inline_images(d, s.fontSize)
p = Paragraph(dmod, s)
h += p.wrap(textBoxWidth, textBoxHeight)[1]
paragraphs.append(p)
@@ -794,7 +911,7 @@ class DividerDrawer(object):
sets = []
for c in pageCards:
setTitle = c.cardset.title()
setTitle = c.cardset
if setTitle not in sets:
sets.append(setTitle)