Source for: lesson8.py [raw]

  1from fonts import adjust_widths_by_letter
  2
  3
  4class Box():
  5
  6    def __init__(self, x=0, y=0, w=1, h=1, stretchy=False, letter="x"):
  7        """Accept arguments to define our box, and store them."""
  8        self.x = x
  9        self.y = y
 10        self.w = w
 11        self.h = h
 12        self.stretchy = stretchy
 13        self.letter = letter
 14
 15    def __repr__(self):
 16        return 'Box(%s, %s, %s, %s, "%s")' % (
 17            self.x, self.y, self.w, self.h, self.letter
 18        )
 19
 20
 21p_and_p = open("pride-and-prejudice.txt").read()
 22text_boxes = []
 23for l in p_and_p:
 24    text_boxes.append(Box(letter=l, stretchy=l == " "))
 25adjust_widths_by_letter(text_boxes)
 26
 27# A few pages all the same size
 28pages = [Box(i * 35, 0, 30, 50) for i in range(10)]
 29
 30# We add a "separation" constant so you can see the boxes individually
 31separation = .05
 32
 33
 34def layout(_boxes):
 35    # Because we modify the box list, we will work on a copy
 36    boxes = _boxes[:]
 37    # We start at page 0
 38    page = 0
 39    # The 1st box should be placed in the correct page
 40    previous = boxes.pop(0)
 41    previous.x = pages[page].x
 42    previous.y = pages[page].y
 43    row = []
 44    while boxes:
 45        # We take the new 1st box
 46        box = boxes.pop(0)
 47        # And put it next to the other
 48        box.x = previous.x + previous.w + separation
 49        # At the same vertical location
 50        box.y = previous.y
 51
 52        # Handle breaking on newlines
 53        break_line = False
 54        # But if it's a newline
 55        if (box.letter == "\n"):
 56            break_line = True
 57            # Newlines take no horizontal space ever
 58            box.w = 0
 59            box.stretchy = False
 60
 61        # Or if it's too far to the right...
 62        elif (box.x + box.w) > (pages[page].x + pages[page].w):
 63            break_line = True
 64            # We adjust the row
 65            slack = (pages[page].x + pages[page].w) - (
 66                row[-1].x + row[-1].w
 67            )
 68            # Get a list of all the ones that are stretchy
 69            stretchies = [b for b in row if b.stretchy]
 70            if not stretchies:  # Nothing stretches do as before.
 71                bump = slack / len(row)
 72                # The 1st box gets 0 bumps, the 2nd gets 1 and so on
 73                for i, b in enumerate(row):
 74                    b.x += bump * i
 75            else:
 76                bump = slack / len(stretchies)
 77                # Each stretchy gets wider
 78                for b in stretchies:
 79                    b.w += bump
 80                # And we put each thing next to the previous one
 81                for j, b in enumerate(row[1:], 1):
 82                    b.x = row[j - 1].x + row[j - 1].w + separation
 83
 84        if break_line:
 85            # We start a new row
 86            row = []
 87            # We go all the way left and a little down
 88            box.x = pages[page].x
 89            box.y = previous.y + previous.h + separation
 90
 91        # But if we go too far down
 92        if box.y + box.h > pages[page].y + pages[page].h:
 93            # We go to the next page
 94            page += 1
 95            # And put the box at the top-left
 96            box.x = pages[page].x
 97            box.y = pages[page].y
 98
 99        # Put the box in the row
100        row.append(box)
101        previous = box
102
103
104layout(text_boxes)
105
106
107import svgwrite
108
109
110def draw_boxes(boxes, fname, size, hide_boxes=False):
111    dwg = svgwrite.Drawing(fname, profile="full", size=size)
112    # Draw the pages
113    for page in pages:
114        dwg.add(
115            dwg.rect(
116                insert=(f"{page.x}cm", f"{page.y}cm"),
117                size=(f"{page.w}cm", f"{page.h}cm"),
118                fill="lightblue",
119            )
120        )
121    # Draw all the boxes
122    for box in boxes:
123        # The box color depends on its features
124        color = "green" if box.stretchy else "red"
125        # Make the colored boxes optional
126        if not hide_boxes:
127            dwg.add(
128                dwg.rect(
129                    insert=(f"{box.x}cm", f"{box.y}cm"),
130                    size=(f"{box.w}cm", f"{box.h}cm"),
131                    fill=color,
132                )
133            )
134        # Display the letter in the box
135        if box.letter:
136            dwg.add(
137                dwg.text(
138                    box.letter,
139                    insert=(f"{box.x}cm", f"{box.y + box.h}cm"),
140                    font_size=f"{box.h}cm",
141                    font_family="Arial",
142                )
143            )
144    dwg.save()
145
146
147draw_boxes(text_boxes, "lesson8.svg", ("30cm", "50cm"), hide_boxes=True)
148