diff --git a/domdiv/card_db/labels_db.json b/domdiv/card_db/labels_db.json index eeef435..0ed011e 100644 --- a/domdiv/card_db/labels_db.json +++ b/domdiv/card_db/labels_db.json @@ -4,14 +4,15 @@ "height": 1.27, "margin-left": 0.75, "margin-top": 1.27, + "name": "Label Avery 8867 Letter", "names": [ + "8867", "5167", "5267", "5667", "5967", "8167", "8667", - "8867", "8927", "15267", "15667", @@ -34,6 +35,7 @@ "height": 1.69, "margin-left": 1.02, "margin-top": 1.08, + "name": "Label Avery L4732 A4", "names": [ "L4732", "L7632" @@ -41,15 +43,16 @@ "pad-horizontal": 0.1, "pad-vertical": 0.1, "paper": "A4", + "tab-height": 1.2, "tab-only": true, "width": 3.56 },{ - "body-height": 1.055, "gap-horizontal": 0.295, "gap-vertical": 0.0, - "height": 1.06, + "height": 2.115, "margin-left": 0.95, "margin-top": 2.15, + "name": "Label Avery L4736 A4", "names": [ "L4736", "L6113" @@ -57,7 +60,24 @@ "pad-horizontal": 0.1, "pad-vertical": 0.1, "paper": "A4", + "tab-height": 1.0, "tab-only": false, "width": 4.53 +},{ + "gap-horizontal": 0.96, + "gap-vertical": 0.42, + "height": 5.93, + "margin-left": 1.74, + "margin-top": 1.47, + "name": "Label Avery Presta 94211 Letter", + "names": [ + "94211" + ], + "pad-horizontal": 0.1, + "pad-vertical": 0.1, + "paper": "LETTER", + "tab-height": 0.9, + "tab-only": false, + "width": 8.57 } ] diff --git a/domdiv/draw.py b/domdiv/draw.py index e59a95e..791c29b 100644 --- a/domdiv/draw.py +++ b/domdiv/draw.py @@ -14,6 +14,7 @@ from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER, TA_LEFT from reportlab.pdfgen import canvas from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfbase.pdfmetrics import stringWidth +from .cards import Card def split(l, n): @@ -24,16 +25,379 @@ def split(l, n): yield l[i:] +class CardPlot(object): + # This object contains information needed to print a divider on a page. + # It goes beyond information about the general card/divider to include page specific drawing information. + # It also includes helpful methods used in manipulating the object and keeping up with tab locations. + + LEFT, CENTRE, RIGHT, TOP, BOTTOM = range(100, 105) # location & directional constants + + tabNumber = 1 # Number of different tab locations + tabIncrement = 0 # Either 1, 0, or -1. Used to select next tab. This can change if tabSerpentine. + tabIncrementStart = 0 # Starting value of tabIncrement + tabStart = 1 # The starting tab location. + tabStartSide = LEFT # The starting side for the tabs + tabSerpentine = False # What to do at the end of a line of tabs. False = start over. True = reverses direction. + lineType = 'line' # Type of outline to use: line, dot, none + cardWidth = 0 # Width of just the divider, with no extra padding/spacing. NEEDS TO BE SET. + cardHeight = 0 # Height of just the divider, with no extra padding/spacing or tab. NEEDS TO BE SET. + tabWidth = 0 # Width of the tab. NEEDS TO BE SET. + tabHeight = 0 # Height of the tab. NEEDS TO BE SET. + wrapper = False # If the divider is a sleeve/wrapper. + + @staticmethod + def tabSetup(tabNumber=None, cardWidth=None, cardHeight=None, tabWidth=None, tabHeight=None, + lineType=None, start=None, serpentine=None, wrapper=None): + # Set up the basic tab information used in calculations when a new CardPlot object is created. + # This needs to be called at least once before the first CardPlot object is created and then it + # needs to be called any time one of the above parameters needs to change. + CardPlot.tabNumber = tabNumber if tabNumber is not None else CardPlot.tabNumber + CardPlot.cardWidth = cardWidth if cardWidth is not None else CardPlot.cardWidth + CardPlot.cardHeight = cardHeight if cardHeight is not None else CardPlot.cardHeight + CardPlot.tabWidth = tabWidth if tabWidth is not None else CardPlot.tabWidth + CardPlot.tabHeight = tabHeight if tabHeight is not None else CardPlot.tabHeight + CardPlot.lineType = lineType if lineType is not None else CardPlot.lineType + CardPlot.tabStartSide = start if start is not None else CardPlot.tabStartSide + CardPlot.tabSerpentine = serpentine if serpentine is not None else CardPlot.tabSerpentine + CardPlot.wrapper = wrapper if wrapper is not None else CardPlot.wrapper + # LEFT tabs RIGHT + # +---+ +---+ +---+ +---+ +---+ + # | 1 | | 2 | | 3 | |...| | N | Note: tabNumber = N, N >=1, 0 is for centred tabs + # + +-+ +-+ +-+ +-+ + + + # Setup first tab as well as starting point and direction of increment for tabs. + if CardPlot.tabStartSide == CardPlot.RIGHT: + CardPlot.tabStart = CardPlot.tabNumber + CardPlot.tabIncrementStart = -1 + elif CardPlot.tabStartSide == CardPlot.CENTRE: + # Get as close to centre as possible + CardPlot.tabStart = (CardPlot.tabNumber + 1) // 2 + CardPlot.tabIncrementStart = 1 + else: + # LEFT and anything else + CardPlot.tabStartSide = CardPlot.LEFT + CardPlot.tabStart = 1 + CardPlot.tabIncrementStart = 1 + + if CardPlot.tabNumber == 1: + CardPlot.tabIncrementStart = 0 + CardPlot.tabIncrement = CardPlot.tabIncrementStart + + @staticmethod + def tabRestart(): + # Resets the tabIncrement to the starting value and returns the starting tabIndex number. + CardPlot.tabIncrement = CardPlot.tabIncrementStart + return CardPlot.tabStart + + def __init__(self, card, x=0, y=0, rotation=0, stackHeight=0, tabIndex=None, page=0, + textTypeFront="card", textTypeBack="rules", + cropOnTop=False, cropOnBottom=False, cropOnLeft=False, cropOnRight=False): + self.card = card + self.x = x # x location of the lower left corner of the card on the page + self.y = y # y location of the lower left corner of the card on the page + self.rotation = rotation # of the card. 0, 90, 180, 270 + self.stackHeight = stackHeight # The height of a stack of these cards. Used for interleaving. + self.tabIndex = tabIndex # Tab location index. Starts at 1 and goes up to CardPlot.tabNumber + self.page = page # holds page number of this printed card + self.textTypeFront = textTypeFront # What card text to put on the front of the divider + self.textTypeBack = textTypeBack # What card text to put on the back of the divider + self.cropOnTop = cropOnTop # When true, cropmarks needed along TOP *printed* edge of the card + self.cropOnBottom = cropOnBottom # When true, cropmarks needed along BOTTOM *printed* edge of the card + self.cropOnLeft = cropOnLeft # When true, cropmarks needed along LEFT *printed* edge of the card + self.cropOnRight = cropOnRight # When true, cropmarks needed along RIGHT *printed* edge of the card + + # And figure out the backside index + if self.tabIndex == 0: + self.tabIndexBack = 0 # Exact Centre special case, so swapping is still exact centre + elif CardPlot.tabNumber == 1: + self.tabIndex = self.tabIndexBack = 1 # There is only one tab, so can only use 1 for both sides + elif 1 <= self.tabIndex <= CardPlot.tabNumber: + self.tabIndexBack = CardPlot.tabNumber + 1 - self.tabIndex + else: + # For anything else, just start at 1 + self.tabIndex = self.tabIndexBack = 1 + + # Now set the offsets and the closest edge to the tab + if self.tabIndex == 0: + # Special case for centred tabs + self.tabOffset = self.tabOffsetBack = (CardPlot.cardWidth - CardPlot.tabWidth) / 2 + self.closestSide = CardPlot.CENTRE + elif CardPlot.tabNumber <= 1: + # If just one tab, then can be right, centre, or left + self.closestSide = CardPlot.tabStartSide + if CardPlot.tabStartSide == CardPlot.RIGHT: + self.tabOffset = CardPlot.cardWidth - CardPlot.tabWidth + self.tabOffsetBack = 0 + elif CardPlot.tabStartSide == CardPlot.CENTRE: + self.tabOffset = (CardPlot.cardWidth - CardPlot.tabWidth) / 2 + self.tabOffsetBack = (CardPlot.cardWidth - CardPlot.tabWidth) / 2 + else: + # LEFT and anything else + self.tabOffset = 0 + self.tabOffsetBack = CardPlot.cardWidth - CardPlot.tabWidth + else: + # More than 1 tabs + self.tabOffset = (self.tabIndex - 1) * ( + (CardPlot.cardWidth - CardPlot.tabWidth) / (CardPlot.tabNumber - 1)) + self.tabOffsetBack = CardPlot.cardWidth - CardPlot.tabWidth - self.tabOffset + + # Set which edge is closest to the tab + if self.tabIndex <= CardPlot.tabNumber / 2: + self.closestSide = CardPlot.LEFT + else: + self.closestSide = CardPlot.RIGHT if self.tabIndex > (CardPlot.tabNumber + 1) / 2 else CardPlot.CENTRE + + def setXY(self, x, y, rotation=None): + # set the card to the given x,y and optional rotation + self.x = x + self.y = y + if rotation is not None: + self.rotation = rotation + + def rotate(self, delta): + # rotate the card by amount delta + self.rotation = (self.rotation + delta) % 360 + + def getTabOffset(self, backside=False): + # Get the tab offset (from the left edge) of the tab given + if backside: + return self.tabOffsetBack + else: + return self.tabOffset + + def nextTab(self, tab=None): + # For a given tab, calculate the next tab in the sequence + tab = tab if tab is not None else self.tabIndex + if CardPlot.tabNumber == 1: + return 1 # it is the same, nothing else to do + + # Increment if in range + if 1 <= tab <= CardPlot.tabNumber: + tab += CardPlot.tabIncrement + + # Now check for wrap around + if tab > CardPlot.tabNumber: + tab = 1 + elif tab < 1: + tab = CardPlot.tabNumber + + if CardPlot.tabSerpentine and CardPlot.tabNumber > 2: + if (tab == 1) or (tab == CardPlot.tabNumber): + # reverse direction for next tab + CardPlot.tabIncrement *= -1 + return tab + + def getClosestSide(self, backside=False): + # Get the closest side for this tab. + # Used when wanting text to be aligned towards the outer edge. + side = self.closestSide + if backside: + # Need to flip + if side == CardPlot.LEFT: + side = CardPlot.RIGHT + elif side == CardPlot.RIGHT: + side = CardPlot.LEFT + return side + + def flipFront2Back(self): + # Flip a card from front to back. i.e., print the front of the divider on the page's back + # and print the back of the divider on the page's front. So what does that mean... + # If it is a wrapper / slipcover, then it is rotated 180 degrees. + # Otherwise, the tab moves from right(left) to left(right). If centre, it stays the same. + # And then the divider's text is moved to the other side of the page. + if self.wrapper: + self.rotate(180) + else: + self.tabIndex, self.tabIndexBack = self.tabIndexBack, self.tabIndex + self.tabOffset, self.tabOffsetBack = self.tabOffsetBack, self.tabOffset + self.textTypeFront, self.textTypeBack = self.textTypeBack, self.textTypeFront + self.closestSide = self.getClosestSide(backside=True) + + def translate(self, canvas, page_width, backside=False): + # Translate the page x,y of the lower left of item, taking into account the rotation, + # and set up the canvas so that (0,0) is now at the lower lower left of the item + # and the item can be drawn as if it is in the "standard" orientation. + # So when done, the canvas is set and ready to draw the divider + x = self.x + y = self.y + rotation = self.rotation + + # set width and height for this card + width = self.cardWidth + height = self.cardHeight + self.tabHeight + if self.wrapper: + height = 2 * (height + self.stackHeight) + + if backside: + x = page_width - x - width + + if self.rotation == 180: + x += width + y += height + elif self.rotation == 90: + if backside: + x += width + rotation = 270 + else: + y += width + elif self.rotation == 270: + if backside: + x += width - height + y += width + rotation = 90 + else: + x += height + + rotation = 360 - rotation % 360 # ReportLab rotates counter clockwise, not clockwise. + canvas.translate(x, y) + canvas.rotate(rotation) + + def translateCropmarkEnable(self, side): + # Returns True if a cropmark is needed on that side of the card + # Takes into account the card's rotation, if the tab is flipped, if the card is next to an edge, etc. + + # First the rotation. The page does not change even if the card is rotated. + # So need to translate page side to the actual drawn card edge + if self.rotation == 0: + sideTop = self.cropOnTop + sideBottom = self.cropOnBottom + sideRight = self.cropOnRight + sideLeft = self.cropOnLeft + elif self.rotation == 90: + sideTop = self.cropOnRight + sideBottom = self.cropOnLeft + sideRight = self.cropOnBottom + sideLeft = self.cropOnTop + elif self.rotation == 180: + sideTop = self.cropOnBottom + sideBottom = self.cropOnTop + sideRight = self.cropOnLeft + sideLeft = self.cropOnRight + elif self.rotation == 270: + sideTop = self.cropOnLeft + sideBottom = self.cropOnRight + sideRight = self.cropOnTop + sideLeft = self.cropOnBottom + + # Now can return the proper value based upon what side is requested + if side == self.TOP: + return sideTop + elif side == self.BOTTOM: + return sideBottom + elif side == self.RIGHT: + return sideRight + elif side == self.LEFT: + return sideLeft + else: + return False # just in case + + +class Plotter(object): + # Creates a simple plotting object that goes from point to point. + # This makes outline drawing easier since calculations only need to be the delta from + # one point to the next. The default plotting in reportlab requires both + # ends of the line in absolute sense. Thus calculations can become increasingly more + # complicated given various options. Using this object simplifies the calculations significantly. + + def __init__(self, canvas, x=0, y=0, cropmarkLength=-1, cropmarkSpacing=-1): + self.canvas = canvas + self.x = x + self.y = y + self.LEFT, self.RIGHT, self.TOP, self.BOTTOM, self.LINE, self.NO_LINE, self.DOT = range(1, 8) # Constants + if cropmarkLength < 0: + cropmarkLength = 0.2 + if cropmarkSpacing < 0: + cropmarkSpacing = 0.1 + self.CropMarkLength = cropmarkLength * cm # The length of a cropmark + self.CropMarkSpacing = cropmarkSpacing * cm # The spacing between the cut point and the start of the cropmark + self.DotSize = 0.2 # Size of dot marks + self.CropEnable = {self.LEFT: False, self.RIGHT: False, self.TOP: False, self.BOTTOM: False} + + def setXY(self, x, y): + self.x = x + self.y = y + + def getXY(self): + return (self.x, self.y) + + def setCropEnable(self, mark, enable=False): + if mark in self.CropEnable: + self.CropEnable[mark] = enable + + def plot(self, delta_x=0, delta_y=0, pen=False, cropmarks=[]): + # Move the pen, drawing along the way + if pen is False: + pen = self.NO_LINE + x, y = self.getXY() # get current point + new_x = x + delta_x # calculate new point from delta + new_y = y + delta_y + if pen == self.LINE: + self.canvas.line(x, y, new_x, new_y) + if pen == self.DOT: + self.canvas.circle(new_x, new_y, self.DotSize) + self.setXY(new_x, new_y) # save the new point + + # Make sure cropmarks is a list + cropmarks = cropmarks if isinstance(cropmarks, list) else [cropmarks] + # Now add any cropmarks + for mark in cropmarks: + # setCropEnable must be called for each direction ahead of time (once per divider). + # Cropmarks are only drawn for directions that are enabled (as set above). + # Each crop mark given is either: + # 1. A tuple of direction and a boolean of additional enablement criteria + # 2. A direction to draw a drop mark + if isinstance(mark, tuple): + direction, enable = mark + enable = enable and self.CropEnable[direction] if direction in self.CropEnable else False + else: + direction = mark + enable = self.CropEnable[direction] if direction in self.CropEnable else False + if direction in self.CropEnable: + self.cropmark(direction, enable) + + def cropmark(self, direction, enabled=False): + # From current point, draw a cropmark in the correct direction and return to starting point + if enabled: + x, y = self.getXY() # Saving for later + + if direction == self.TOP: + self.plot(0, self.CropMarkSpacing) + self.plot(0, self.CropMarkLength, self.LINE) + if direction == self.BOTTOM: + self.plot(0, -self.CropMarkSpacing) + self.plot(0, -self.CropMarkLength, self.LINE) + if direction == self.RIGHT: + self.plot(self.CropMarkSpacing, 0) + self.plot(self.CropMarkLength, 0, self.LINE) + if direction == self.LEFT: + self.plot(-self.CropMarkSpacing, 0) + self.plot(-self.CropMarkLength, 0, self.LINE) + self.setXY(x, y) # Restore to starting point + + class DividerDrawer(object): - def __init__(self): - self.odd = True + def __init__(self, options=None): self.canvas = None - self.options = None + self.pages = None + self.options = options @staticmethod def get_image_filepath(fname): return pkg_resources.resource_filename('domdiv', os.path.join('images', fname)) + def draw(self, cards=[], options=None): + if options is not None: + self.options = options + + self.registerFonts() + self.canvas = canvas.Canvas( + self.options.outfile, + pagesize=(self.options.paperwidth, self.options.paperheight)) + self.drawDividers(cards) + if self.options.info or self.options.info_all: + self.drawInfo() + self.canvas.save() + def registerFonts(self): # the following are filenames from both an Adobe Reader install and a download from fontsgeek fontfilenames = ['MinionPro-Regular.ttf', @@ -164,167 +528,239 @@ class DividerDrawer(object): def wantCentreTab(self, card): return (card.isExpansion() and self.options.centre_expansion_dividers) or self.options.tab_side == "centre" - def getOutline(self, card): - - dividerWidth = self.options.dividerWidth - dividerHeight = self.options.dividerHeight - dividerBaseHeight = self.options.dividerBaseHeight - tabLabelWidth = self.options.labelWidth - notch_height = self.options.notch_height # thumb notch height - notch_width1 = self.options.notch_width1 # thumb notch width: top away from tab - notch_width2 = self.options.notch_width2 # thumb notch width: bottom on side of tab - - theTabHeight = dividerHeight - dividerBaseHeight - theTabWidth = self.options.labelWidth - - if self.wantCentreTab(card): - side_2_tab = (dividerWidth - theTabWidth) / 2 - else: - side_2_tab = 0 - - nonTabWidth = dividerWidth - tabLabelWidth - side_2_tab - - def DeltaXYtoLines(delta): - result = [] - started = False - for x, y in delta: - if not started: - last_x = x - last_y = y - started = True - else: - result.append((last_x, last_y, last_x + x, last_y + y)) - last_x = last_x + x - last_y = last_y + y - return result - + def drawOutline(self, item, isBack=False): + # draw outline or cropmarks + if isBack and not self.options.cropmarks: + return + if self.options.linewidth <= 0.0: + return self.canvas.saveState() + self.canvas.setLineWidth(self.options.linewidth) - if not self.options.wrapper: + # The back is flipped + if isBack: + self.canvas.translate(item.cardWidth, 0) + self.canvas.scale(-1, 1) + + plotter = Plotter(self.canvas, + cropmarkLength=self.options.cropmarkLength, + cropmarkSpacing=self.options.cropmarkSpacing) + + dividerWidth = item.cardWidth + dividerHeight = item.cardHeight + item.tabHeight + dividerBaseHeight = item.cardHeight + tabLabelWidth = item.tabWidth + theTabWidth = item.tabWidth + theTabHeight = item.tabHeight + + left2tab = item.getTabOffset(backside=isBack) + right2tab = dividerWidth - tabLabelWidth - left2tab + nearZero = 0.01 + left2tab = left2tab if left2tab > nearZero else 0 + right2tab = right2tab if right2tab > nearZero else 0 + + if item.lineType.lower() == 'line': + lineType = plotter.LINE + lineTypeNoDot = plotter.LINE + elif item.lineType.lower() == 'dot': + lineType = plotter.DOT + lineTypeNoDot = plotter.NO_LINE + else: + lineType = plotter.NO_LINE + lineTypeNoDot = plotter.NO_LINE + + # Setup bare minimum lineStyle's + lineStyle = [lineType for i in range(0, 10)] + lineStyle[0] = lineTypeNoDot + lineStyle[7] = lineType + lineStyle[8] = lineType if left2tab > 0 else lineTypeNoDot + lineStyle[9] = lineType if right2tab > 0 else lineTypeNoDot + + RIGHT = plotter.RIGHT + LEFT = plotter.LEFT + BOTTOM = plotter.BOTTOM + TOP = plotter.TOP + NO_LINE = plotter.NO_LINE + + plotter.setCropEnable(RIGHT, self.options.cropmarks and item.translateCropmarkEnable(item.RIGHT)) + plotter.setCropEnable(LEFT, self.options.cropmarks and item.translateCropmarkEnable(item.LEFT)) + plotter.setCropEnable(TOP, self.options.cropmarks and item.translateCropmarkEnable(item.TOP)) + plotter.setCropEnable(BOTTOM, self.options.cropmarks and item.translateCropmarkEnable(item.BOTTOM)) + + if not item.wrapper: # Normal Card Outline - # + F+-------------------+E - # | | - # H+-----------------------+G D+-----+C - # | | - # | Generic Divider | - # | Tab Centered or to the Side | - # | | - # A+-------------------------------------------------+B - # delta x delta y - delta = [(0, 0), # to A - (dividerWidth, 0), # A to B - (0, dividerBaseHeight), # B to C - (-side_2_tab, 0), # C to D - (0, theTabHeight), # D to E - (-theTabWidth, 0), # E to F - (0, -theTabHeight), # F to G - (-nonTabWidth, 0), # G to H - (0, -dividerBaseHeight)] # H to A - self.canvas.lines(DeltaXYtoLines(delta)) + # <-left2tab-> <--tabLabelWidth--> <-right2tab-> + # | | | | + # Z-+ F7-------------------7E +-Y + # | | + # H-8------------8 9-------------9-C + # | G D | + # | Generic Divider | + # | Tab Centered or to the Side | + # | | + # A-7------------0-------------------0-------------7-B + # | V| W| | + # + plotter.plot(0, 0, NO_LINE, [LEFT, BOTTOM]) # ? to A + plotter.plot(left2tab, 0, lineStyle[0], BOTTOM) # A to V + plotter.plot(theTabWidth, 0, lineStyle[0], BOTTOM) # V to W + plotter.plot(right2tab, 0, lineStyle[7], [BOTTOM, RIGHT]) # W to B + plotter.plot(0, dividerBaseHeight, lineStyle[9], RIGHT) # B to C + plotter.plot(-right2tab, 0, lineStyle[9]) # C to D + plotter.plot(0, theTabHeight, lineStyle[7], TOP) # D to E + plotter.plot(right2tab, 0, NO_LINE, [TOP, RIGHT]) # E to Y + plotter.plot(-right2tab, 0, NO_LINE) # Y to E + plotter.plot(-theTabWidth, 0, lineStyle[7], TOP) # E to F + plotter.plot(0, -theTabHeight, lineStyle[8]) # F to G + plotter.plot(-left2tab, 0, lineStyle[8], LEFT) # G to H + plotter.plot(0, theTabHeight, NO_LINE, [TOP, LEFT]) # H to Z + plotter.plot(0, -theTabHeight, NO_LINE) # Z to H + plotter.plot(0, -dividerBaseHeight, lineStyle[7]) # H to A else: # Card Wrapper Outline - notch_width3 = notch_width1 # thumb notch width: bottom away from tab - stackHeight = card.getStackHeight(self.options.thickness) + + # Set up values used in the outline + minNotch = 0.1 * cm # Don't really want notches that are smaller than this. + if self.options.notch_length * cm > minNotch: + # A notch length was given, so notches are wanted + notch_height = self.options.notch_height * cm # thumb notch height + notch1 = notch2 = notch3 = notch4 = self.options.notch_length * cm # thumb notch width + notch1used = notch2used = notch3used = notch4used = True # For now + else: + # No notches are wanted + notch_height = 0 + notch1 = notch2 = notch3 = notch4 = 0 + notch1used = notch2used = notch3used = notch4used = False + + # Even if wanted, there may not be room, and limit to one pair of notches + if (right2tab - minNotch < notch1) or not notch1used: + notch1 = 0 + notch1used = False + if (left2tab - minNotch < notch4) or not notch4used or notch1used: + notch4 = notch2 = 0 + notch4used = notch2used = False + else: + notch3 = 0 + notch3used = False + + # Setup the rest of the lineStyle's + lineStyle[1] = lineType if notch1used else lineTypeNoDot + lineStyle[2] = lineType if notch2used else lineTypeNoDot + lineStyle[3] = lineType if notch3used else lineTypeNoDot + lineStyle[4] = lineType if notch4used else lineTypeNoDot + lineStyle[5] = lineType if notch1used and right2tab > 0 else lineTypeNoDot + lineStyle[6] = lineType if notch4used and left2tab > 0 else lineTypeNoDot + + stackHeight = item.stackHeight body_minus_notches = dividerBaseHeight - (2.0 * notch_height) - tab_2_notch = dividerWidth - theTabWidth - side_2_tab - notch_width1 - if (tab_2_notch < 0): - tab_2_notch = dividerWidth - theTabWidth - side_2_tab - notch_width1 = 0 - # + U+-------------------+T + - # | | - # + V+. . . . . . . . . .+S + - # | | - # + X+---------------------+W . . . . . . . . R+----+Q - # | | - # Z+-------+Y +P - # | | - # | Generic Wrapper | - # | Normal Side | - # | | - # AA+-------+BB N+-------+O - # | | - # + +CC. . . . . . . . . . . . . . . . . .M+ + - # | | - # + +DD. . . . . . . . . . . . . . . . . .L+ + - # | | - # FF+-------+EE K+-------+J - # | | - # | Reverse Side | - # | rotated 180 | - # | | - # A+-------+B +I - # | | - # + C+---------------------+D G+----+H - # | | - # + E+-------------------+F + - # - # delta x delta y - delta = [(0, theTabHeight + notch_height), # to A - (notch_width1, 0), # A to B - (0, -notch_height), # B to C - (tab_2_notch, 0), # C to D - (0, -theTabHeight), # D to E - (theTabWidth, 0), # E to F - (0, theTabHeight), # F to G - (side_2_tab, 0), # G to H - (0, notch_height), # H to I - (0, body_minus_notches), # I to J - (-notch_width2, 0), # J to K - (0, notch_height), # K to L - (0, stackHeight), # L to M - (0, notch_height), # M to N - (notch_width2, 0), # N to O - (0, body_minus_notches), # O to P - (0, notch_height), # P to Q - (-side_2_tab, 0), # Q to R - (0, stackHeight), # R to S - (0, theTabHeight), # S to T - (-theTabWidth, 0), # T to U - (0, -theTabHeight), # U to V - (0, -stackHeight), # V to W - (-tab_2_notch, 0), # W to X - (0, -notch_height), # X to Y - (-notch_width1, 0), # Y to Z - (0, -body_minus_notches), # Z to AA - (notch_width3, 0), # AA to BB - (0, -notch_height), # BB to CC - (0, -stackHeight), # CC to DD - (0, -notch_height), # DD to EE - (-notch_width3, 0), # EE to FF - (0, -body_minus_notches)] # FF to A + tab2notch1 = right2tab - notch1 + tab2notch4 = left2tab - notch4 - self.canvas.lines(DeltaXYtoLines(delta)) + # <-----left2tab----------> <--tabLabelWidth--> <-----right2tab--------> + # | | | | | | + # Zb-+ Va+ V7-------------------7U +Ua +-Ub + # <--tab2notch4->| |<--tab2notch1-> + # + W0...................0T + # Y | | R + # Za-+ 8---------------8...................9---------------9 +-Pa + # | X S | + # Z-6---------4Ya Q1--------5-P + # | | + # | Generic Wrapper | + # | Normal Side | + # | | + # AA-2--------2BB N3--------3-O + # | | + # + 0CC.................................................M0 + + # | | + # + 0DD.................................................L0 + + # | | + # FF-2--------2EE K3--------3-J + # | | + # | Reverse Side | + # | rotated 180 | + # | Ca H | + # GG-6---------4<--tab2notch4-> <--tab2notch1->1--------5-I + # | C F | + # B-+ Cb8---------------8 9---------------1G +-Ia + # | | + # -+A Cc+ D7-------------------7E +Ga +-Ib + # | | | | | | + # <-----left2tab----------> <--tabLabelWidth--> <-----right2tab--------> + plotter.plot(0, 0, NO_LINE, [BOTTOM, LEFT]) # ? to A + plotter.plot(0, theTabHeight, NO_LINE, LEFT) # A to B + plotter.plot(0, notch_height, NO_LINE, (LEFT, notch4used or notch1used)) # B to GG + plotter.plot(notch4, 0, lineStyle[4]) # GG to Ca + plotter.plot(0, -notch_height, lineStyle[8]) # Ca to Cb + plotter.plot(0, -theTabHeight, NO_LINE, (BOTTOM, notch4used or notch2used)) # Cb to Cc + plotter.plot(0, theTabHeight, NO_LINE) # Cc to Cb + plotter.plot(tab2notch4, 0, lineStyle[8]) # Cb to C + plotter.plot(0, -theTabHeight, lineStyle[7], BOTTOM) # C to D + plotter.plot(tabLabelWidth, 0, lineStyle[7], BOTTOM) # D to E + plotter.plot(0, theTabHeight, lineStyle[9]) # E to F + plotter.plot(tab2notch1, 0, lineStyle[1]) # F to G + plotter.plot(0, -theTabHeight, NO_LINE, (BOTTOM, notch1used or notch3used)) # G to Ga + plotter.plot(0, theTabHeight, NO_LINE) # Ga to G + plotter.plot(0, notch_height, lineStyle[1]) # G to H + plotter.plot(notch1, 0, lineStyle[5], (RIGHT, notch1used or notch4used)) # H to I + plotter.plot(0, -notch_height, NO_LINE, RIGHT) # I to Ia + plotter.plot(0, -theTabHeight, NO_LINE, [RIGHT, BOTTOM]) # Ia to Ib + plotter.plot(0, theTabHeight, NO_LINE) # Ib to Ia + plotter.plot(0, notch_height, NO_LINE) # Ia to I + plotter.plot(0, body_minus_notches, lineStyle[3], (RIGHT, notch2used or notch3used)) # I to J + plotter.plot(-notch3, 0, lineStyle[3]) # J to K + plotter.plot(0, notch_height, lineStyle[0]) # K to L + plotter.plot(0, stackHeight, lineStyle[0]) # L to M + plotter.plot(0, notch_height, lineStyle[3]) # M to N + plotter.plot(notch3, 0, lineStyle[3], (RIGHT, notch2used or notch3used)) # N to O + plotter.plot(0, body_minus_notches, lineStyle[5], (RIGHT, notch1used or notch4used)) # O to P + plotter.plot(0, notch_height, NO_LINE, RIGHT) # P to Pa + plotter.plot(0, -notch_height, NO_LINE) # Pa to P + plotter.plot(-notch1, 0, lineStyle[1]) # P to Q + plotter.plot(0, notch_height, lineStyle[9]) # Q to R + plotter.plot(-tab2notch1, 0, lineStyle[9]) # R to S + plotter.plot(0, stackHeight, lineStyle[0]) # S to T + plotter.plot(0, theTabHeight, lineStyle[7], TOP) # S to U + plotter.plot(tab2notch1, 0, NO_LINE, (TOP, notch1used or notch3used)) # U to Ua + plotter.plot(notch1, 0, NO_LINE, [TOP, RIGHT]) # Ua to Ub + plotter.plot(-notch1, 0, NO_LINE) # Ub to Ua + plotter.plot(-tab2notch1, 0, NO_LINE) # Ua to U + plotter.plot(-theTabWidth, 0, lineStyle[7], TOP) # U to V + plotter.plot(-tab2notch4, 0, NO_LINE, (TOP, notch4used or notch2used)) # V to Va + plotter.plot(tab2notch4, 0, NO_LINE) # Va to V + plotter.plot(0, -theTabHeight, lineStyle[0]) # V to W + plotter.plot(0, -stackHeight, lineStyle[8]) # W to X + plotter.plot(-tab2notch4, 0, lineStyle[8]) # X to Y + plotter.plot(0, -notch_height, lineStyle[4]) # Y to Ya + plotter.plot(-notch4, 0, lineStyle[6], (LEFT, notch1used or notch4used)) # Ya to Z + plotter.plot(0, notch_height, NO_LINE, LEFT) # Z to Za + plotter.plot(0, theTabHeight + stackHeight, NO_LINE, [TOP, LEFT]) # Za to Zb + plotter.plot(0, -theTabHeight - stackHeight, NO_LINE) # Zb to Za + plotter.plot(0, -notch_height, NO_LINE) # Za to Z + plotter.plot(0, -body_minus_notches, lineStyle[2], (LEFT, notch2used or notch3used)) # Z to AA + plotter.plot(notch2, 0, lineStyle[2]) # AA to BB + plotter.plot(0, -notch_height, lineStyle[0]) # BB to CC + plotter.plot(0, -stackHeight, lineStyle[0]) # CC to DD + plotter.plot(0, -notch_height, lineStyle[2]) # DD to EE + plotter.plot(-notch2, 0, lineStyle[2], (LEFT, notch2used or notch3used)) # EE to FF + plotter.plot(0, -body_minus_notches, lineStyle[6]) # FF to GG + + # Add fold lines self.canvas.setStrokeGray(0.9) - self.canvas.line(dividerWidth - side_2_tab, - dividerHeight + dividerBaseHeight + stackHeight, - dividerWidth - side_2_tab - theTabWidth, - dividerHeight + dividerBaseHeight + stackHeight) - self.canvas.line( - dividerWidth - side_2_tab, dividerHeight + dividerBaseHeight + - 2 * stackHeight, dividerWidth - side_2_tab - theTabWidth, - dividerHeight + dividerBaseHeight + 2 * stackHeight) - self.canvas.line(notch_width1, dividerHeight, - dividerWidth - notch_width2, dividerHeight) - self.canvas.line(notch_width1, dividerHeight + stackHeight, - dividerWidth - notch_width2, - dividerHeight + stackHeight) + plotter.setXY(left2tab, dividerHeight + stackHeight + dividerBaseHeight) # ? to X + plotter.plot(theTabWidth, 0, plotter.LINE) # X to S + plotter.plot(0, stackHeight) # S to T + plotter.plot(-theTabWidth, 0, plotter.LINE) # V to S + + plotter.setXY(notch2, dividerHeight) # ? to DD + plotter.plot(dividerWidth - notch2 - notch3, 0, plotter.LINE) # DD to L + plotter.plot(0, stackHeight) # L to M + plotter.plot(-dividerWidth + notch2 + notch3, 0, plotter.LINE) # M to CC self.canvas.restoreState() - def draw(self, cards, options): - self.options = options - - self.registerFonts() - self.canvas = canvas.Canvas( - options.outfile, - pagesize=(options.paperwidth, options.paperheight)) - self.drawDividers(cards) - if options.info or options.info_all: - self.drawInfo() - self.canvas.save() - def add_inline_images(self, text, fontsize): def replace_image_tag(text, fontsize, @@ -415,83 +851,6 @@ class DividerDrawer(object): return text.strip().strip('\n') - def drawOutline(self, - card, - x, - y, - rightSide, - isBack=False): - # draw outline or cropmarks - - # Don't draw anything if zero (or less) line width - if self.options.linewidth <= 0.0: - return - - self.canvas.saveState() - self.canvas.setLineWidth(self.options.linewidth) - cropmarksright = (x == self.options.numDividersHorizontal - 1) - cropmarksleft = (x == 0) - if rightSide: - self.canvas.translate(self.options.dividerWidth, 0) - self.canvas.scale(-1, 1) - if not self.options.cropmarks and not isBack: - # don't draw outline on back, in case lines don't line up with - # front - self.getOutline(card) - - elif self.options.cropmarks and not self.options.wrapper: - cmw = 0.5 * cm - - # Horizontal-line cropmarks - mirror = cropmarksright and not rightSide or cropmarksleft and rightSide - if mirror: - self.canvas.saveState() - self.canvas.translate(self.options.dividerWidth, 0) - self.canvas.scale(-1, 1) - if cropmarksleft or cropmarksright: - self.canvas.line(-2 * cmw, 0, -cmw, 0) - self.canvas.line(-2 * cmw, self.options.dividerBaseHeight, - -cmw, self.options.dividerBaseHeight) - if y > 0: - self.canvas.line(-2 * cmw, self.options.dividerHeight, - -cmw, self.options.dividerHeight) - if mirror: - self.canvas.restoreState() - - # Vertical-line cropmarks - - # want to always draw the right-edge and middle-label-edge lines.. - # ...and draw the left-edge if this is the first card on the left - - # ...but we need to take mirroring into account, to know "where" - # to draw the left / right lines... - if rightSide: - leftLine = self.options.dividerWidth - rightLine = 0 - else: - leftLine = 0 - rightLine = self.options.dividerWidth - middleLine = self.options.dividerWidth - self.options.labelWidth - - if y == 0: - self.canvas.line(rightLine, -2 * cmw, rightLine, -cmw) - self.canvas.line(middleLine, -2 * cmw, middleLine, -cmw) - if cropmarksleft: - self.canvas.line(leftLine, -2 * cmw, leftLine, -cmw) - if y == self.options.numDividersVertical - 1: - self.canvas.line(rightLine, self.options.dividerHeight + cmw, - rightLine, - self.options.dividerHeight + 2 * cmw) - self.canvas.line(middleLine, self.options.dividerHeight + cmw, - middleLine, - self.options.dividerHeight + 2 * cmw) - if cropmarksleft: - self.canvas.line( - leftLine, self.options.dividerHeight + cmw, leftLine, - self.options.dividerHeight + 2 * cmw) - - self.canvas.restoreState() - def drawCardCount(self, card, x, y, offset=-1): # Note that this is right justified. # x represents the right most for the image (image grows to the left) @@ -613,35 +972,30 @@ class DividerDrawer(object): fontSize - 2) return w - def drawTab(self, card, rightSide, wrapper="no"): + def drawTab(self, item, wrapper="no", backside=False): + card = item.card # Skip blank cards if card.isBlank(): return # draw tab flap self.canvas.saveState() + + translate_y = item.cardHeight if self.wantCentreTab(card): - translate_x = self.options.dividerWidth / 2 - self.options.labelWidth / 2 - translate_y = self.options.dividerHeight - self.options.labelHeight - elif not rightSide: - translate_x = self.options.dividerWidth - self.options.labelWidth - translate_y = self.options.dividerHeight - self.options.labelHeight + translate_x = item.cardWidth / 2 - item.tabWidth / 2 else: - translate_x = 0 - translate_y = self.options.dividerHeight - self.options.labelHeight + translate_x = item.getTabOffset(backside=backside) if wrapper == "back": - translate_y = self.options.labelHeight + translate_y = item.tabHeight if self.wantCentreTab(card): - translate_x = self.options.dividerWidth / 2 + self.options.labelWidth / 2 - elif not rightSide: - translate_x = self.options.dividerWidth + translate_x = item.cardWidth / 2 + item.tabWidth / 2 else: - translate_x = self.options.labelWidth + translate_x = item.getTabOffset(backside=False) + item.tabWidth if wrapper == "front": - translate_y = translate_y + self.options.dividerHeight + 2.0 * card.getStackHeight( - self.options.thickness) + translate_y = translate_y + item.cardHeight + item.tabHeight + 2.0 * item.stackHeight self.canvas.translate(translate_x, translate_y) @@ -651,15 +1005,15 @@ class DividerDrawer(object): if self.options.black_tabs: self.canvas.saveState() self.canvas.setFillColorRGB(0, 0, 0) - self.canvas.rect(0, 0, self.options.labelWidth, self.options.labelHeight, fill=True) + self.canvas.rect(0, 0, item.tabWidth, item.tabHeight, fill=True) self.canvas.restoreState() # allow for 3 pt border on each side - textWidth = self.options.labelWidth - 6 + textWidth = item.tabWidth - 6 textHeight = 7 if self.options.no_tab_artwork: textHeight = 4 - textHeight = self.options.labelHeight / 2 - textHeight + \ + textHeight = item.tabHeight / 2 - textHeight + \ card.getType().getTabTextHeightOffset() # draw banner @@ -669,8 +1023,8 @@ class DividerDrawer(object): DividerDrawer.get_image_filepath(img), 1, 0, - self.options.labelWidth - 2, - self.options.labelHeight - 1, + item.tabWidth - 2, + item.tabHeight - 1, preserveAspectRatio=False, anchor='n', mask='auto') @@ -699,7 +1053,7 @@ class DividerDrawer(object): if setText is None: setText = "" - self.canvas.drawCentredString(self.options.labelWidth - 10, + self.canvas.drawCentredString(item.tabWidth - 10, textHeight + 2, setText) textInsetRight = 15 else: @@ -707,7 +1061,7 @@ class DividerDrawer(object): if setImage and 'tab' in self.options.set_icon: setImageHeight = 3 + card.getType().getTabTextHeightOffset() - self.drawSetIcon(setImage, self.options.labelWidth - 20, + self.drawSetIcon(setImage, item.tabWidth - 20, setImageHeight) textInsetRight = 20 @@ -744,14 +1098,15 @@ class DividerDrawer(object): words = line.split() NotRightEdge = ( not self.options.tab_name_align == "right" and - (self.options.tab_name_align == "centre" or rightSide or + (self.options.tab_name_align == "centre" or + item.getClosestSide(backside=backside) != CardPlot.RIGHT or not self.options.tab_name_align == "edge")) if wrapper == "back" and not self.options.tab_name_align == "centre": NotRightEdge = not NotRightEdge if NotRightEdge: - if self.options.tab_name_align == "centre" or self.wantCentreTab(card): - w = self.options.labelWidth / 2 - self.nameWidth( - line, fontSize) / 2 + if (self.options.tab_name_align == "centre" or self.wantCentreTab(card) + or (item.getClosestSide(backside=backside) == CardPlot.CENTRE)): + w = item.tabWidth / 2 - self.nameWidth(line, fontSize) / 2 else: w = textInset @@ -770,11 +1125,10 @@ class DividerDrawer(object): else: # 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 + w = item.tabWidth / 2 - self.nameWidth(line, fontSize) / 2 + w = item.tabWidth - w else: - w = self.options.labelWidth - textInsetRight + w = item.tabWidth - textInsetRight # to make tabs easier to read when grouped together extra 3pt is for # space between text + set symbol @@ -797,18 +1151,15 @@ class DividerDrawer(object): if wrapper == "front" and card.getCardCount() >= 5: # Print smaller version of name on the top wrapper edge - self.canvas.translate(0, -card.getStackHeight( - self.options.thickness)) # move into area used by the wrapper + self.canvas.translate(0, -item.stackHeight) # move into area used by the wrapper fontSize = 8 # use the smallest font self.canvas.setFont(self.font_mapping['Regular'], fontSize) textHeight = fontSize - 2 - textHeight = card.getStackHeight( - self.options.thickness) / 2 - textHeight / 2 + textHeight = item.stackHeight / 2 - textHeight / 2 h = textHeight words = name.split() - w = self.options.labelWidth / 2 - self.nameWidth(name, - fontSize) / 2 + w = item.tabWidth / 2 - self.nameWidth(name, fontSize) / 2 def drawWordPiece(text, fontSize): self.canvas.setFont(self.font_mapping['Regular'], fontSize) @@ -825,28 +1176,27 @@ class DividerDrawer(object): self.canvas.restoreState() - def drawText(self, card, divider_text="card", wrapper="no"): + def drawText(self, item, divider_text="card", wrapper="no"): + card = item.card # Skip blank cards if card.isBlank(): return self.canvas.saveState() usedHeight = 0 - totalHeight = self.options.dividerHeight - self.options.labelHeight + totalHeight = item.cardHeight # Figure out if any translation needs to be done if wrapper == "back": - self.canvas.translate(self.options.dividerWidth, - self.options.dividerHeight) + self.canvas.translate(item.cardWidth, item.cardHeight + item.tabHeight) self.canvas.rotate(180) if wrapper == "front": - self.canvas.translate(0, self.options.dividerHeight + - card.getStackHeight(self.options.thickness)) + self.canvas.translate(0, item.cardHeight + item.tabHeight + item.stackHeight) if wrapper == "front" or wrapper == "back": - if self.options.notch_width1 > 0: - usedHeight += self.options.notch_height + if self.options.notch_length > 0: + usedHeight += self.options.notch_height * cm # Add 'body-top' items drewTopIcon = False @@ -855,7 +1205,7 @@ class DividerDrawer(object): Image_x_left += self.drawCost(card, Image_x_left, totalHeight - usedHeight - 0.5 * cm) drewTopIcon = True - Image_x_right = self.options.dividerWidth - 4 + Image_x_right = item.cardWidth - 4 if 'body-top' in self.options.set_icon and not card.isExpansion(): setImage = card.setImage() if setImage: @@ -874,11 +1224,11 @@ class DividerDrawer(object): # 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 + right_margin = item.cardWidth - 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 + w = item.cardWidth / 2 + textWidth = item.cardWidth - 2 * worst_margin + textWidth2 = item.cardWidth - left_margin - right_margin # Calculate font size that will fit in the area # Start with centering type. But if the fontSize gets too small @@ -929,7 +1279,7 @@ class DividerDrawer(object): textHorizontalMargin = .5 * cm textVerticalMargin = .3 * cm - textBoxWidth = self.options.dividerWidth - 2 * textHorizontalMargin + textBoxWidth = item.cardWidth - 2 * textHorizontalMargin textBoxHeight = totalHeight - usedHeight - 2 * textVerticalMargin spacerHeight = 0.2 * cm minSpacerHeight = 0.05 * cm @@ -968,51 +1318,52 @@ class DividerDrawer(object): self.canvas.restoreState() - def drawDivider(self, - card, - x, - y, - isBack=False, - divider_text="card", - divider_text2="rules"): - # figure out whether the tab should go on the right side or not - if self.options.tab_side == "right": - rightSide = isBack - elif self.options.tab_side in ["left", "full"]: - rightSide = not isBack - else: - # alternate the cards - if not isBack: - rightSide = not self.odd - else: - rightSide = self.odd + def drawDivider(self, item, isBack=False, horizontalMargin=-1, verticalMargin=-1): + # First save canvas state + self.canvas.saveState() + + # Make sure we use the right margins + if horizontalMargin < 0: + horizontalMargin = self.options.horizontalMargin + if verticalMargin < 0: + verticalMargin = self.options.verticalMargin # apply the transforms to get us to the corner of the current card self.canvas.resetTransforms() - self.canvas.translate(self.options.horizontalMargin, - self.options.verticalMargin) + pageWidth = self.options.paperwidth - (2 * horizontalMargin) + self.canvas.translate(horizontalMargin, verticalMargin) if isBack: self.canvas.translate(self.options.back_offset, self.options.back_offset_height) - self.canvas.translate(x * self.options.dividerWidthReserved, - y * self.options.dividerHeightReserved) + pageWidth -= 2 * self.options.back_offset + + item.translate(self.canvas, pageWidth, isBack) # actual drawing if not self.options.tabs_only: - self.drawOutline(card, x, y, rightSide, isBack) + self.drawOutline(item, isBack) if self.options.wrapper: wrap = "front" + isBack = False # Safety. If a wrapper, there is no backside else: wrap = "no" - self.drawTab(card, rightSide, wrapper=wrap) - if not self.options.tabs_only: - self.drawText(card, divider_text, wrapper=wrap) - if self.options.wrapper: - self.drawTab(card, rightSide, wrapper="back") - self.drawText(card, divider_text2, wrapper="back") - def drawSetNames(self, pageCards): + cardText = item.textTypeFront + if isBack: + cardText = item.textTypeBack + + self.drawTab(item, wrapper=wrap, backside=isBack) + if not self.options.tabs_only: + self.drawText(item, cardText, wrapper=wrap) + if item.wrapper: + self.drawTab(item, wrapper="back", backside=True) + self.drawText(item, item.textTypeBack, wrapper="back") + + # retore the canvas state to the way we found it + self.canvas.restoreState() + + def drawSetNames(self, pageItems): # print sets for this page self.canvas.saveState() @@ -1057,8 +1408,8 @@ class DividerDrawer(object): yPos = layout['minMarginHeight'] sets = [] - for c in pageCards: - setTitle = c.cardset + for item in pageItems: + setTitle = item.card.cardset.title() if setTitle not in sets: sets.append(setTitle) @@ -1075,64 +1426,287 @@ class DividerDrawer(object): finally: self.canvas.restoreState() - def drawDividers(self, cards): - # split into pages - cards = split(cards, self.options.numDividersVertical * - self.options.numDividersHorizontal) + def calculatePages(self, cards): + options = self.options - # Starting with tabs on the left or the right? - if self.options.tab_side in ["right-alternate", "right"]: - self.odd = True + # Adjust for Vertical vs Horizontal + if options.orientation == "vertical": + options.dividerWidth, options.dividerBaseHeight = options.dominionCardHeight, options.dominionCardWidth else: - # left-alternate, left, full - self.odd = False + options.dividerWidth, options.dividerBaseHeight = options.dominionCardWidth, options.dominionCardHeight - for pageNum, pageCards in enumerate(cards): - # remember whether we start with odd or even divider for tab - # location - pageStartOdd = self.odd + options.fixedMargins = False + options.spin = 0 + options.label = options.label if 'label' in options else None + if options.label is not None: + # Set Margins + options.minmarginheight = (options.label['margin-top'] + options.label['pad-vertical']) * cm + options.minmarginwidth = (options.label['margin-left'] + options.label['pad-horizontal']) * cm + # Set Label size + options.labelHeight = (options.label['tab-height'] - 2 * options.label['pad-vertical']) * cm + options.labelWidth = (options.label['width'] - 2 * options.label['pad-horizontal']) * cm + # Set spacing between labels + options.verticalBorderSpace = (options.label['gap-vertical'] + 2 * options.label['pad-vertical']) * cm + options.horizontalBorderSpace = (options.label['gap-horizontal'] + 2 * options.label['pad-horizontal']) * cm + # Fix up other settings + options.fixedMargins = True + options.dividerBaseHeight = options.label['body-height'] * cm + options.dividerWidth = options.labelWidth + options.rotate = 0 + options.dominionCardWidth = options.dividerWidth + options.dominionCardHeight = options.dividerBaseHeight + if options.orientation == "vertical": + # Spin the card. This is similar to a rotate, but given a label has a fixed location on the page + # the divider must change shape and rotation. Rotate can't be used directly, + # since that is used in the calculation of where to place the dividers on the page. + # This 'spins' the divider only, but keeps all the other calcuations the same. + options.spin = 270 + # Now fix up the card dimentions. + options.dominionCardWidth = options.labelHeight + options.label['body-height'] * cm + options.dominionCardHeight = options.labelWidth - options.label['tab-height'] * cm + options.labelWidth = options.dominionCardWidth + # Need to swap now because they will be swapped again later because "vertical" + options.dominionCardWidth, options.dominionCardHeight = (options.dominionCardHeight, + options.dominionCardWidth) + # Fix up the label dimentions + if options.tab_side != "full": + options.labelWidth = options.tabwidth * cm + + else: + # Margins already set + # Set Label size + options.labelHeight = .9 * cm + options.labelWidth = options.tabwidth * cm + if options.tab_side == "full" or options.labelWidth > options.dividerWidth: + options.labelWidth = options.dividerWidth + # Set spacing between labels + options.verticalBorderSpace = options.vertical_gap * cm + options.horizontalBorderSpace = options.horizontal_gap * cm + + # Set Height + options.dividerHeight = options.dividerBaseHeight + options.labelHeight + + # Start building up the space reserved for each divider + options.dividerWidthReserved = options.dividerWidth + options.dividerHeightReserved = options.dividerHeight + + if options.wrapper: + # Adjust height for wrapper. Use the maximum thickness of any divider so we know anything will fit. + maxStackHeight = max(c.getStackHeight(options.thickness) for c in cards) + options.dividerHeightReserved = 2 * (options.dividerHeightReserved + maxStackHeight) + print("Max Card Stack Height: {:.2f}cm ".format(maxStackHeight/10.0)) + + # Adjust for rotation + if options.rotate == 90 or options.rotate == 270: + # for page calculations, this just means switching horizontal and vertical for these rotations. + options.dividerWidth, options.dividerHeight = options.dividerHeight, options.dividerWidth + options.dividerWidthReserved, options.dividerHeightReserved = (options.dividerHeightReserved, + options.dividerWidthReserved) + + options.dividerWidthReserved += options.horizontalBorderSpace + options.dividerHeightReserved += options.verticalBorderSpace + + # as we don't draw anything in the final border, it shouldn't count towards how many tabs we can fit + # so it gets added back in to the page size here + numDividersVerticalP = int( + (options.paperheight - 2 * options.minmarginheight + options.verticalBorderSpace) / + options.dividerHeightReserved) + numDividersHorizontalP = int( + (options.paperwidth - 2 * options.minmarginwidth + options.horizontalBorderSpace) / + options.dividerWidthReserved) + numDividersVerticalL = int( + (options.paperwidth - 2 * options.minmarginwidth + options.verticalBorderSpace) / + options.dividerHeightReserved) + numDividersHorizontalL = int( + (options.paperheight - 2 * options.minmarginheight + options.horizontalBorderSpace) / + options.dividerWidthReserved) + + if ((numDividersVerticalL * numDividersHorizontalL > numDividersVerticalP * + numDividersHorizontalP) and not options.fixedMargins) and options.rotate == 0: + options.numDividersVertical = numDividersVerticalL + options.numDividersHorizontal = numDividersHorizontalL + options.minHorizontalMargin = options.minmarginheight + options.minVerticalMargin = options.minmarginwidth + options.paperheight, options.paperwidth = options.paperwidth, options.paperheight + else: + options.numDividersVertical = numDividersVerticalP + options.numDividersHorizontal = numDividersHorizontalP + options.minHorizontalMargin = options.minmarginheight + options.minVerticalMargin = options.minmarginwidth + + assert options.numDividersVertical > 0, "Could not vertically fit the divider on the page" + assert options.numDividersHorizontal > 0, "Could not horizontally fit the divider on the page" + + if not options.fixedMargins: + # dynamically max margins + options.horizontalMargin = (options.paperwidth - options.numDividersHorizontal * + options.dividerWidthReserved + options.horizontalBorderSpace) / 2 + options.verticalMargin = (options.paperheight - options.numDividersVertical * + options.dividerHeightReserved + options.verticalBorderSpace) / 2 + else: + options.horizontalMargin = options.minmarginwidth + options.verticalMargin = options.minmarginheight + + items = self.setupCardPlots(options, cards) # Turn cards into items to plot + self.pages = self.convert2pages(options, items) # plot items into pages + + def setupCardPlots(self, options, cards=[]): + # First, set up common information for the dividers + # Doing a lot of this up front, while the cards are ordered + # just in case the dividers need to be reordered on the page. + # By setting up first, any tab or text flipping will be correct, + # even if the divider moves around a bit on the pages. + + # Drawing line type + if options.cropmarks: + if 'dot' in options.linetype.lower(): + lineType = 'dot' # Allow the DOTs if requested + else: + lineType = 'no_line' + else: + lineType = options.linetype.lower() + + # Starting with tabs on the left, right, or centre? + if "right" in options.tab_side: + tabSideStart = CardPlot.RIGHT # right, right-alternate, right-flip + elif "left" in options.tab_side: + tabSideStart = CardPlot.LEFT # left, left-alternate, left-flip + elif "centre" in options.tab_side: + tabSideStart = CardPlot.CENTRE # centre + elif "full" == options.tab_side: + tabSideStart = CardPlot.CENTRE # full + else: + tabSideStart = CardPlot.LEFT # catch anything else + + cardWidth = options.dominionCardWidth + cardHeight = options.dominionCardHeight + + # Adjust for Vertical + if options.orientation == "vertical": + cardWidth, cardHeight = cardHeight, cardWidth + + # Initialized CardPlot tabs + CardPlot.tabSetup(tabNumber=options.tab_number, + cardWidth=cardWidth, + cardHeight=cardHeight, + lineType=lineType, + tabWidth=options.labelWidth, + tabHeight=options.labelHeight, + start=tabSideStart, + serpentine=options.tab_serpentine, + wrapper=options.wrapper) + + # Now go through all the cards and create their plotter information record... + items = [] + nextTabIndex = CardPlot.tabRestart() + lastCardSet = None + reset_expansion_tabs = options.expansion_dividers and options.expansion_reset_tabs + + for card in cards: + # Check if tab needs to be reset to the start + if reset_expansion_tabs and not card.isExpansion(): + if lastCardSet != card.cardset_tag: + # In a new expansion, so reset the tabs to start over + nextTabIndex = CardPlot.tabRestart() + if options.tab_number > Card.sets[card.cardset_tag]['count']: + # Limit to the number of tabs to the number of dividers in the expansion + CardPlot.tabSetup(tabNumber=Card.sets[card.cardset_tag]['count']) + elif CardPlot.tabNumber != options.tab_number: + # Make sure tabs are set back to the original + CardPlot.tabSetup(tabNumber=options.tab_number) + lastCardSet = card.cardset_tag + + if self.wantCentreTab(card): + # If we want centred expansion cards, then force this divider to centre + thisTabIndex = 0 + else: + thisTabIndex = nextTabIndex + + item = CardPlot(card, + rotation=options.spin if options.spin != 0 else options.rotate, + tabIndex=thisTabIndex, + textTypeFront=options.text_front, + textTypeBack=options.text_back, + stackHeight=card.getStackHeight(options.thickness) + ) + if options.flip and (options.tab_number == 2) and (thisTabIndex != CardPlot.tabStart): + item.flipFront2Back() # Instead of flipping the tab, flip the whole divider front to back + + # Before moving on, setup the tab for the next item if this tab slot was used + if thisTabIndex == nextTabIndex: + nextTabIndex = item.nextTab(nextTabIndex) # already used, so move on to the next tab + + items.append(item) + return items + + def convert2pages(self, options, items=[]): + # Take the layout and all the items and separate the items into pages. + # Each item will have all its plotting information filled in. + rows = options.numDividersVertical + columns = options.numDividersHorizontal + numPerPage = rows * columns + # Calculate if there is always enough room for horizontal and vertical crop marks + RoomForCropH = options.horizontalBorderSpace > 2*(options.cropmarkLength + options.cropmarkSpacing) * cm + RoomForCropV = options.verticalBorderSpace > 2*(options.cropmarkLength + options.cropmarkSpacing) * cm + + items = split(items, numPerPage) + pages = [] + for pageNum, pageItems in enumerate(items): + page = [] + for i in range(numPerPage): + if pageItems and i < len(pageItems): + # Given a CardPlot object called item, its number on the page, and the page number + # Return/set the items x,y,rotation, crop mark settings, and page number + # For x,y assume the canvas has already been adjusted for the margins + x = i % columns + y = (rows - 1) - (i // columns) + pageItems[i].x = x * options.dividerWidthReserved + pageItems[i].y = y * options.dividerHeightReserved + pageItems[i].cropOnTop = (y == rows - 1) or RoomForCropV + pageItems[i].cropOnBottom = (y == 0) or RoomForCropV + pageItems[i].cropOnLeft = (x == 0) or RoomForCropH + pageItems[i].cropOnRight = (x == columns - 1) or RoomForCropH + # pageItems[i].rotation = 0 + pageItems[i].page = pageNum + 1 + page.append(pageItems[i]) + + pages.append((options.horizontalMargin, options.verticalMargin, page)) + return pages + + def drawDividers(self, cards=[]): + if not self.pages: + self.calculatePages(cards) + + # Now go page by page and print the dividers + for pageNum, pageInfo in enumerate(self.pages): + hMargin, vMargin, page = pageInfo + + # Front page footer if not self.options.no_page_footer and ( not self.options.tabs_only and self.options.order != "global"): - self.drawSetNames(pageCards) + self.drawSetNames(page) - for i, card in enumerate(pageCards): - # print card - x = i % self.options.numDividersHorizontal - y = i // self.options.numDividersHorizontal - self.canvas.saveState() - self.drawDivider(card, - x, - self.options.numDividersVertical - 1 - y, - isBack=False, - divider_text=self.options.text_front, - divider_text2=self.options.text_back) - self.canvas.restoreState() - self.odd = not self.odd + # Front page + for item in page: + # print the dividor + self.drawDivider(item, isBack=False, horizontalMargin=hMargin, verticalMargin=vMargin) self.canvas.showPage() if pageNum + 1 == self.options.num_pages: break if self.options.tabs_only or self.options.text_back == "none" or self.options.wrapper: # Don't print the sheets with the back of the dividers continue + + # back page footer if not self.options.no_page_footer and self.options.order != "global": - self.drawSetNames(pageCards) - # start at same oddness - self.odd = pageStartOdd - for i, card in enumerate(pageCards): - # print card - x = (self.options.numDividersHorizontal - 1 - i - ) % self.options.numDividersHorizontal - y = i // self.options.numDividersHorizontal - self.canvas.saveState() - self.drawDivider(card, - x, - self.options.numDividersVertical - 1 - y, - isBack=True, - divider_text=self.options.text_back) - self.canvas.restoreState() - self.odd = not self.odd + self.drawSetNames(page) + + # Back page + for item in page: + # print the dividor + self.drawDivider(item, isBack=True, horizontalMargin=hMargin, verticalMargin=vMargin) self.canvas.showPage() if pageNum + 1 == self.options.num_pages: break diff --git a/domdiv/main.py b/domdiv/main.py index 730d17b..5e3783e 100644 --- a/domdiv/main.py +++ b/domdiv/main.py @@ -21,10 +21,11 @@ from .draw import DividerDrawer LOCATION_CHOICES = ["tab", "body-top", "hide"] NAME_ALIGN_CHOICES = ["left", "right", "centre", "edge"] TAB_SIDE_CHOICES = ["left", "right", "left-alternate", "right-alternate", - "centre", "full"] + "left-flip", "right-flip", "centre", "full"] TEXT_CHOICES = ["card", "rules", "blank"] +LINE_CHOICES = ["line", "dot", "cropmarks", "dot-cropmarks"] + EDITION_CHOICES = ["1", "2", "latest", "all"] -LABEL_CHOICES = ["8867", "L4732", "L4736"] EXPANSION_CHOICES = ["adventures", "alchemy", "base", "cornucopia", "dark ages", "dominion1stEdition", "dominion2ndEdition", "dominion2ndEditionUpgrade", @@ -58,6 +59,26 @@ def get_languages(path): LANGUAGE_CHOICES = get_languages("card_db") +def get_resource_stream(path): + return codecs.EncodedFile(pkg_resources.resource_stream('domdiv', path), "utf-8") + + +# Load Label information +LABEL_INFO = None +LABEL_CHOICES = [] +LABEL_KEYS = [] +LABEL_SELECTIONS = [] +labels_db_filepath = os.path.join("card_db", "labels_db.json") +with get_resource_stream(labels_db_filepath) as labelfile: + LABEL_INFO = json.loads(labelfile.read().decode('utf-8')) +assert LABEL_INFO, "Could not load label information from database" +for label in LABEL_INFO: + if len(label['names']) > 0: + LABEL_KEYS.append(label['names'][0]) + LABEL_SELECTIONS.append(label['name'] if 'name' in label else label['names'][0]) + LABEL_CHOICES.extend(label['names']) + + def add_opt(options, option, value): assert not hasattr(options, option) setattr(options, option, value) @@ -165,11 +186,33 @@ def parse_opts(cmdline_args=None): dest="tab_side", default="right-alternate", help="Alignment of tab; " - "'left'/'right' forces all tabs to left/right side; " - "'left-alternate' will start on the left and then toggle between left and right for the tabs; " - "'right-alternate' will start on the right and then toggle between right and left for the tabs; " - "'centre' will force all label tabs to the centre; " - "'full' will force all label tabs to be full width of the divider.") + "'left'/'right'/'centre' sets the starting side of the tabs; " + "'full' will force all label tabs to be full width of the divider; sets --tab_number 1 " + "'left-alternate' will start on the left and then toggle between left and right for the tabs," + " sets --tab_number 2; " + "'right-alternate' will start on the right and then toggle between right and left for the tabs," + " sets --tab_number 2; " + "'left-flip' like left-alternate, but the right will be flipped front/back with tab on left," + " sets --tab_number 2; " + "'right-flip' like right-alternate, but the left will be flipped front/back with tab on right," + " sets --tab_number 2; ") + group_tab.add_argument( + "--tab-number", + type=int, + default=1, + help="The number of tabs. When set to 1, all tabs are on the same side (specified by --tab_side). " + "When set to 2, tabs will alternate between left and right. (starting side specified by --tab_side). " + "When set > 2, the first tab will be on left/right side specified by --tab_side, then the rest " + "of the tabs will be evenly spaced until ending on the opposite side. Then the cycle repeats. " + "May be overriden by some options of --tab_side.") + group_tab.add_argument( + "--tab-serpentine", + action="store_true", + help="Affects the order of tabs. When not selected, tabs will progress from the starting side (left/right) " + "to the opposite side (right/left), and then repeat (e.g., left to right, left to right, etc.). " + "When selected, the order is changed to smoothly alternate between the two sides " + "(e.g., left to right, to left, to right, etc.) " + "Only valid if --tab_number > 2.") group_tab.add_argument( "--tab-name-align", choices=NAME_ALIGN_CHOICES + ["center"], @@ -227,6 +270,12 @@ def parse_opts(cmdline_args=None): action="store_true", dest="centre_expansion_dividers", help='Centre the tabs on expansion dividers.') + group_expansion.add_argument( + "--expansion-reset-tabs", + action="store_true", + dest="expansion_reset_tabs", + help="When set, the tabs are restarted (left/right) at the beginning of each expansion. " + "If not set, the tab pattern will continue from one expansion to the next. ") group_expansion.add_argument( "--expansion-dividers-long-name", action="store_true", @@ -427,9 +476,37 @@ def parse_opts(cmdline_args=None): action="store_true", help="In tabs-only mode, draw tabs on black background" ) + group_printing.add_argument( + "--linetype", + choices=LINE_CHOICES, + dest="linetype", + default="line", + help="The divider outline type. " + "'line' will print a solid line outlining the divider; " + "'dot' will print a dot at each corner of the divider; " + "'cropmarks' will print cropmarks for the divider; " + "'dot-cropmarks' will combine 'dot' and 'cropmarks'") + group_printing.add_argument( + "--cropmarkLength", + type=float, + default=0.2, + help="Length of actual drawn cropmark in centimeters.") + group_printing.add_argument( + "--cropmarkSpacing", + type=float, + default=0.1, + help="Spacing between card and the start of the cropmark in centimeters.") + group_printing.add_argument( + "--rotate", + type=int, + choices=[0, 90, 180, 270], + default=0, + help="Divider degrees of rotation relative to the page edge. " + "No optimization will be done on the number of dividers per page.") group_printing.add_argument( "--label", dest="label_name", + choices=LABEL_CHOICES, default=None, help="Use preset label dimentions. Specify a label name. " "This will override settings that conflict with the preset label settings.") @@ -475,6 +552,43 @@ def parse_opts(cmdline_args=None): def clean_opts(options): + + if "center" in options.tab_side: + options.tab_side = str(options.tab_side).replace("center", "centre") + + if "center" in options.tab_name_align: + options.tab_name_align = str(options.tab_name_align).replace("center", "centre") + + if options.tab_side == "full" and options.tab_name_align == "edge": + # This case does not make sense since there are two tab edges in this case. So picking left edge. + print("** Warning: Aligning card name as 'left' for 'full' tabs **") + options.tab_name_align = "left" + + if options.tab_number < 1: + print("** Warning: --tab-number must be 1 or greater. Setting to 1. **") + options.tab_number = 1 + + if options.tab_side == "full" and options.tab_number != 1: + options.tab_number = 1 # Full is 1 big tab + + if "-alternate" in options.tab_side: + if options.tab_number != 2: + print("** Warning: --tab-side with 'alternate' implies 2 tabs. Setting --tab-number to 2 **") + options.tab_number = 2 # alternating left and right, so override tab_number + + if "-flip" in options.tab_side: + # for left and right tabs + if options.tab_number != 2: + print("** Warning: --tab-side with 'flip' implies 2 tabs. Setting --tab-number to 2 **") + options.tab_number = 2 # alternating left and right with a flip, so override tab_number + options.flip = True + else: + options.flip = False + + if options.tab_number < 3 and options.tab_serpentine: + print("** Warning: --tab-serpentine only valid if --tab-number > 2. **") + options.tab_serpentine = False + if options.sleeved_thick: options.thickness = 3.2 options.sleeved = True @@ -486,6 +600,19 @@ def clean_opts(options): if options.notch: options.notch_length = 1.5 + if options.notch_length > 0: + options.notch_height = 0.25 # thumb notch height + + if options.cropmarks and options.linetype == 'line': + options.linetype = 'cropmarks' + + if options.linetype == 'cropmarks': + options.cropmarks = True + + if options.linetype == 'dot-cropmarks': + options.linetype = 'dot' + options.cropmarks = True + if options.expansions is None: # No instance given, so default to all Official expansions options.expansions = ['*'] @@ -512,12 +639,7 @@ def clean_opts(options): options.label = None if options.label_name is not None: - # Load the Labels, and look for match - labels_db_filepath = os.path.join("card_db", "labels_db.json") - with get_resource_stream(labels_db_filepath) as labelfile: - label_info = json.loads(labelfile.read().decode('utf-8')) - assert label_info, "Could not load label information from database" - for label in label_info: + for label in LABEL_INFO: if options.label_name.upper() in [n.upper() for n in label['names']]: options.label = label break @@ -528,23 +650,38 @@ def clean_opts(options): label = options.label label['paper'] = label['paper'] if 'paper' in label else "LETTER" label['tab-only'] = label['tab-only'] if 'tab-only' in label else True - label['body-height'] = label['body-height'] if 'body-height' in label else 0 + label['tab-height'] = label['tab-height'] if 'tab-height' in label else label['height'] + label['body-height'] = label['body-height'] if 'body-height' in label else label['height'] - label['tab-height'] label['gap-vertical'] = label['gap-vertical'] if 'gap-vertical' in label else 0.0 label['gap-horizontal'] = label['gap-horizontal'] if 'gap-horizontal' in label else 0.0 label['pad-vertical'] = label['pad-vertical'] if 'pad-vertical' in label else 0.1 label['pad-horizontal'] = label['pad-horizontal'] if 'pad-horizontal' in label else 0.1 # Option Overrides when using labels + MIN_BODY_CM_FOR_COUNT = 0.6 + MIN_BODY_CM_FOR_TEXT = 4.0 + MIN_HEIGHT_CM_FOR_VERTICAL = 5.0 + MIN_WIDTH_CM_FOR_FULL = 5.0 + options.linewidth = 0.0 + options.cropmarks = False + options.wrapper = False options.papersize = label['paper'] if label['tab-only']: options.tabs_only = True - if label['body-height'] < 4.0: + if label['body-height'] < MIN_BODY_CM_FOR_TEXT: + # Not enough room for any text options.text_front = "blank" options.text_back = "blank" - if label['body-height'] < 1.0: + if label['body-height'] < MIN_BODY_CM_FOR_COUNT: + # Not enough room for count and type options.count = False options.types = False + if label['height'] < MIN_HEIGHT_CM_FOR_VERTICAL: + # Not enough room to make vertical + options.orientation = "horizontal" + if (options.label['width'] - 2 * options.label['pad-horizontal']) < MIN_WIDTH_CM_FOR_FULL: + options.tab_side = "full" options.label = label return options @@ -608,10 +745,6 @@ def parse_cardsize(spec, sleeved): return dominionCardWidth, dominionCardHeight -def get_resource_stream(path): - return codecs.EncodedFile(pkg_resources.resource_stream('domdiv', path), "utf-8") - - def find_index_of_object(lst=[], attributes={}): # Returns the index of the first object in lst that matches the given attributes. Otherwise returns None. # attributes is a dict of key: value pairs. Object attributes that are lists are checked to have value in them. @@ -957,8 +1090,10 @@ def filter_sort_cards(cards, options): for card in cards: if card.cardset_tag == 'dominion2ndEditionUpgrade': card.cardset_tag = 'dominion1stEdition' + options.expansions.append(card.cardset_tag.lower()) elif card.cardset_tag == 'intrigue2ndEditionUpgrade': card.cardset_tag = 'intrigue1stEdition' + options.expansions.append(card.cardset_tag.lower()) # Combine all Events across all expansions if options.exclude_events: @@ -1194,6 +1329,7 @@ def filter_sort_cards(cards, options): exp_name = exp count = randomizerCountByExpansion[exp] + Card.sets[set_tag]['count'] = count if 'no_randomizer' in set_values: if set_values['no_randomizer']: count = 0 @@ -1230,119 +1366,15 @@ def filter_sort_cards(cards, options): def calculate_layout(options, cards=[]): + # This is in place to allow for test cases to it call directly to get + options = clean_opts(options) + options.dominionCardWidth, options.dominionCardHeight = parse_cardsize(options.size, options.sleeved) + options.paperwidth, options.paperheight = parse_papersize(options.papersize) + options.minmarginwidth, options.minmarginheight = parseDimensions(options.minmargin) - dominionCardWidth, dominionCardHeight = parse_cardsize(options.size, - options.sleeved) - paperwidth, paperheight = parse_papersize(options.papersize) - - if options.orientation == "vertical": - dividerWidth, dividerBaseHeight = dominionCardHeight, dominionCardWidth - else: - dividerWidth, dividerBaseHeight = dominionCardWidth, dominionCardHeight - - if options.tab_name_align == "center": - options.tab_name_align = "centre" - - if options.tab_side == "full" and options.tab_name_align == "edge": - # This case does not make sense since there are two tab edges in this case. So picking left edge. - print("** Warning: Aligning card name as 'left' for 'full' tabs **", file=sys.stderr) - options.tab_name_align = "left" - - fixedMargins = False - options.label = options.label if 'label' in options else None - if options.label is not None: - # Set Margins - minmarginheight = (options.label['margin-top'] + options.label['pad-vertical']) * cm - minmarginwidth = (options.label['margin-left'] + options.label['pad-horizontal']) * cm - # Set Label size - labelHeight = (options.label['height'] - 2 * options.label['pad-vertical']) * cm - labelWidth = (options.label['width'] - 2 * options.label['pad-horizontal']) * cm - # Set spacing between labels - verticalBorderSpace = (options.label['gap-vertical'] + 2 * options.label['pad-vertical']) * cm - horizontalBorderSpace = (options.label['gap-horizontal'] + 2 * options.label['pad-horizontal']) * cm - # Fix up other settings - dividerBaseHeight = options.label['body-height'] * cm - dividerWidth = labelWidth - fixedMargins = True - else: - minmarginwidth, minmarginheight = parseDimensions(options.minmargin) - if options.tab_side == "full": - labelWidth = dividerWidth - else: - labelWidth = options.tabwidth * cm - labelHeight = .9 * cm - horizontalBorderSpace = options.horizontal_gap * cm - verticalBorderSpace = options.vertical_gap * cm - - dividerHeight = dividerBaseHeight + labelHeight - - dividerWidthReserved = dividerWidth + horizontalBorderSpace - dividerHeightReserved = dividerHeight + verticalBorderSpace - if options.wrapper: - max_card_stack_height = max(c.getStackHeight(options.thickness) - for c in cards) - dividerHeightReserved = (dividerHeightReserved * 2) + ( - max_card_stack_height * 2) - print("Max Card Stack Height: {:.2f}cm ".format(max_card_stack_height)) - - # Notch measurements - notch_height = 0.25 * cm # thumb notch height - notch_width1 = options.notch_length * cm # thumb notch width: top away from tab - notch_width2 = 0.00 * cm # thumb notch width: bottom on side of tab - - add_opt(options, 'dividerWidth', dividerWidth) - add_opt(options, 'dividerHeight', dividerHeight) - add_opt(options, 'dividerBaseHeight', dividerBaseHeight) - add_opt(options, 'dividerWidthReserved', dividerWidthReserved) - add_opt(options, 'dividerHeightReserved', dividerHeightReserved) - add_opt(options, 'labelWidth', labelWidth) - add_opt(options, 'labelHeight', labelHeight) - add_opt(options, 'notch_height', notch_height) - add_opt(options, 'notch_width1', notch_width1) - add_opt(options, 'notch_width2', notch_width2) - - # as we don't draw anything in the final border, it shouldn't count towards how many tabs we can fit - # so it gets added back in to the page size here - numDividersVerticalP = int( - (paperheight - 2 * minmarginheight + verticalBorderSpace) / - options.dividerHeightReserved) - numDividersHorizontalP = int( - (paperwidth - 2 * minmarginwidth + horizontalBorderSpace) / - options.dividerWidthReserved) - numDividersVerticalL = int( - (paperwidth - 2 * minmarginwidth + verticalBorderSpace) / - options.dividerHeightReserved) - numDividersHorizontalL = int( - (paperheight - 2 * minmarginheight + horizontalBorderSpace) / - options.dividerWidthReserved) - - if ((numDividersVerticalL * numDividersHorizontalL > numDividersVerticalP * - numDividersHorizontalP) and not fixedMargins): - add_opt(options, 'numDividersVertical', numDividersVerticalL) - add_opt(options, 'numDividersHorizontal', numDividersHorizontalL) - add_opt(options, 'paperheight', paperwidth) - add_opt(options, 'paperwidth', paperheight) - add_opt(options, 'minHorizontalMargin', minmarginheight) - add_opt(options, 'minVerticalMargin', minmarginwidth) - else: - add_opt(options, 'numDividersVertical', numDividersVerticalP) - add_opt(options, 'numDividersHorizontal', numDividersHorizontalP) - add_opt(options, 'paperheight', paperheight) - add_opt(options, 'paperwidth', paperwidth) - add_opt(options, 'minHorizontalMargin', minmarginheight) - add_opt(options, 'minVerticalMargin', minmarginwidth) - - if not fixedMargins: - # dynamically max margins - add_opt(options, 'horizontalMargin', - (options.paperwidth - options.numDividersHorizontal * - options.dividerWidthReserved + horizontalBorderSpace) / 2) - add_opt(options, 'verticalMargin', - (options.paperheight - options.numDividersVertical * - options.dividerHeightReserved + verticalBorderSpace) / 2) - else: - add_opt(options, 'horizontalMargin', minmarginwidth) - add_opt(options, 'verticalMargin', minmarginheight) + dd = DividerDrawer(options) + dd.calculatePages(cards) + return dd def generate(options): @@ -1352,7 +1384,7 @@ def generate(options): cards = filter_sort_cards(cards, options) assert cards, "No cards after filtering/sorting" - calculate_layout(options, cards) + dd = calculate_layout(options, cards) print("Paper dimensions: {:.2f}cm (w) x {:.2f}cm (h)".format( options.paperwidth / cm, options.paperheight / cm)) @@ -1363,8 +1395,7 @@ def generate(options): print("Margins: {:.2f}cm h, {:.2f}cm v\n".format( options.horizontalMargin / cm, options.verticalMargin / cm)) - dd = DividerDrawer() - dd.draw(cards, options) + dd.draw(cards) def main(): diff --git a/domdiv/tests/text_tab_tests.py b/domdiv/tests/text_tab_tests.py index dd67fee..ff42d8e 100644 --- a/domdiv/tests/text_tab_tests.py +++ b/domdiv/tests/text_tab_tests.py @@ -256,6 +256,7 @@ def test_tab_edge_full(): ['--tab-name-align', 'edge', '--tab-side', 'full']) assert options.tab_name_align == 'edge' assert options.tab_side == 'full' + options = main.clean_opts(options) main.calculate_layout(options) assert options.tab_name_align == 'left' # special check for odd condition assert options.tab_side == 'full' @@ -316,6 +317,7 @@ def test_tab_center_left(): options = main.parse_opts(['--tab-name-align', 'center', '--tab-side', 'left']) assert options.tab_name_align == 'center' assert options.tab_side == 'left' + options = main.clean_opts(options) main.calculate_layout(options) assert options.tab_name_align == 'centre' # check for change in value assert options.tab_side == 'left'