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