Source for: lesson7.py [raw]

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