Created
March 23, 2018 18:03
-
-
Save ralsina/fed163f378cd0ce9a8150c5fbca99d49 to your computer and use it in GitHub Desktop.
Exploratory work
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import svgwrite | |
class Box(): | |
def __init__(self, x=0, y=0, w=1, h=1, red=False, blue=False, yellow=False, letter=None): | |
self.x = x | |
self.y = y | |
self.w = w | |
self.h = h | |
self.red = red | |
self.blue = blue | |
self.yellow = yellow | |
self.letter = letter | |
def __str__(self): | |
return self.letter or 'Box' | |
def __repr__(self): | |
return self.letter or 'Box' | |
big_box = Box(0, 0, 80, 1000) | |
pages = [big_box] | |
def draw_boxes(boxes, with_boxes=True): | |
STYLES = """ | |
.red { fill: red;} | |
.green { fill: green;} | |
.blue {fill: blue;} | |
.yellow {fill: yellow;} | |
.bigbox {fill: cyan; } | |
""" | |
dwg = svgwrite.Drawing('test.svg', profile='full', size=('%d' % big_box.w, '%d' % big_box.w)) | |
dwg.defs.add(dwg.style(STYLES)) | |
for bb in pages: | |
dwg.add(dwg.rect(insert=(bb.x,bb.y), size=(bb.w, bb.h), class_='bigbox')) | |
for box in boxes: | |
if with_boxes: | |
if box.red: | |
dwg.add(dwg.rect(insert=(box.x, box.y), size=(box.w, box.h), class_='red')) | |
elif box.blue: | |
dwg.add(dwg.rect(insert=(box.x, box.y), size=(box.w, box.h), class_='blue')) | |
elif box.yellow: | |
dwg.add(dwg.rect(insert=(box.x, box.y), size=(box.w, box.h), class_='yellow')) | |
else: | |
dwg.add(dwg.rect(insert=(box.x, box.y), size=(box.w, box.h), class_='green')) | |
if box.letter: | |
dwg.add(dwg.text(box.letter, insert=(box.x, box.y + box.h), font_size=box.h, font_family='Arial')) | |
dwg.save() | |
# So, how could we layout the many boxes inside the big_box? | |
many_boxes = [Box(0, 0, 1, 1) for _ in range(5000)] | |
# Do nothing ... | |
# They are all in the same place | |
# draw_boxes(many_boxes) | |
# Try to lay them out side by side | |
# They just go too wide | |
# We add a "separation" constant so you can see the boxes individually | |
separation = .2 | |
def layout1(boxes): | |
for i, box in enumerate(boxes): | |
box.x = i * (1 + separation) | |
# Put each one next to the other until they reach a width, | |
# then go down. This is a monospaced layout. | |
def layout2(boxes): | |
for i, box in enumerate(boxes[1:]): | |
box.x = boxes[i-1].x + 1 + separation | |
box.y = boxes[i-1].y | |
if box.x > big_box.w: | |
box.x = 0 | |
box.y = boxes[i-1].y + 1.1 | |
# Now, what happens if some boxes are wider than the others? | |
# layout2 will make them overlap or have wide breaks between them | |
import random | |
many_boxes = [Box(0, 0, 1+random.randint(-2,2)/10, 1) for _ in range(5000)] | |
# So, we use each box's width instead of a fixed width | |
def layout3(boxes): | |
for i, box in enumerate(boxes[1:], 1): | |
prev_box = boxes[i-1] | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if box.x > big_box.w: | |
box.x = 0 | |
box.y = prev_box.y + 1.1 | |
# layout3(many_boxes) | |
# But the right side is ragged | |
# We can, when we break, put the remaining space spread between boxes | |
# This is a "justified" layout | |
def layout4(boxes): | |
last_break = 0 | |
for i, box in enumerate(boxes[1:], 1): | |
prev_box = boxes[i-1] | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if box.x > big_box.w: | |
box.x = 0 | |
box.y = prev_box.y + 1.1 | |
slack = big_box.w - (prev_box.x + prev_box.w) | |
mini_slack = slack / (i-last_break) | |
for j, b in enumerate(boxes[last_break:i]): | |
b.x += j * mini_slack | |
last_break = i | |
# layout4(many_boxes) | |
# But what happens if some boxes are red? | |
for box in many_boxes: | |
if random.randint(1,6) > 5: | |
box.red = True | |
# Well, nothing much, other than they are red | |
# But what if red means "stretchy" and only those can stretch? | |
def layout5(boxes): | |
last_break = 0 | |
for i, box in enumerate(boxes[1:], 1): | |
prev_box = boxes[i-1] | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if box.x > big_box.w: | |
box.x = 0 | |
box.y = prev_box.y + 1.1 | |
slack = big_box.w - (prev_box.x + prev_box.w) | |
row = boxes[last_break:i] | |
reds = [b for b in row if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
last_break = i | |
# But what happens if a few are blue? | |
for box in many_boxes: | |
if random.randint(1,150) > 149: | |
box.blue = True | |
# Well, nothing much, other than they are blue | |
# But what if blue means "this row ends here"? | |
def layout6(boxes): | |
last_break = 0 | |
for i, box in enumerate(boxes[1:], 1): | |
prev_box = boxes[i-1] | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or box.x > big_box.w: | |
box.x = 0 | |
box.y = prev_box.y + 1.1 | |
slack = big_box.w - (prev_box.x + prev_box.w) | |
row = boxes[last_break:i] | |
reds = [b for b in row if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
last_break = i | |
# Some reds get reeeeeeealy stretchy! That is because rows that | |
# end because of a blue have very few boxes. So maybe we don't stretch those? | |
def layout7(boxes): | |
last_break = 0 | |
for i, box in enumerate(boxes[1:], 1): | |
prev_box = boxes[i-1] | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or box.x > big_box.w: | |
box.x = 0 | |
box.y = prev_box.y + 1.1 | |
if not prev_box.blue: | |
row = boxes[last_break:i] | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
reds = [b for b in row if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
last_break = i | |
# What if we want blue boxes break lines but also separate lines a little? | |
def layout8(boxes): | |
last_break = 0 | |
for i, box in enumerate(boxes[1:], 1): | |
prev_box = boxes[i-1] | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or box.x > big_box.w: | |
box.x = 0 | |
if prev_box.blue: | |
box.y = prev_box.y + 2.1 | |
else: # not blue | |
box.y = prev_box.y + 1.1 | |
row = boxes[last_break:i] | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
reds = [b for b in row if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
last_break = i | |
# So ... what if each box has a letter or a space inside it? | |
for box in many_boxes: | |
# More than one space so they appear often | |
box.letter = random.choice(' abcdefghijklmnopqrstuvwxyz') | |
# Maybe we should make the box sizes depend on the letter inside it? | |
# This is complicated, sorry | |
import harfbuzz as hb | |
import freetype2 as ft | |
def adjust_widths_by_letter(boxes): | |
buf = hb.Buffer.create() | |
buf.add_str(''.join(b.letter for b in boxes)) | |
buf.guess_segment_properties() | |
font_lib = ft.get_default_lib() | |
face = font_lib.find_face('Arial') | |
face.set_char_size(size = 1, resolution=64) | |
font = hb.Font.ft_create(face) | |
hb.shape(font, buf) | |
# at this point buf.glyph_positions has all the data we need | |
for box, position in zip(boxes, buf.glyph_positions): | |
box.w = position.x_advance | |
adjust_widths_by_letter(many_boxes) | |
# layout8(many_boxes) | |
# There is all that space between letters we added when they were boxes. Let's remove it. | |
separation = 0 | |
layout8(many_boxes) | |
# How about we get the letters from a text instead of randomly? | |
p_and_p = open('pride-and-prejudice.txt').read() | |
text_boxes = [] | |
for l in p_and_p: | |
text_boxes.append(Box(letter=l)) | |
adjust_widths_by_letter(text_boxes) | |
# layout8(text_boxes) | |
# Oh, it's all green now, and it's all one thing after another. We should make newlines blue! | |
for b in text_boxes: | |
if b.letter == '\n': | |
b.blue = True | |
# layout8(text_boxes) | |
# Better, but newlines should not really take any space should they? | |
def add_blue(boxes): | |
for b in boxes: | |
if b.letter == '\n': | |
b.blue = True | |
b.w = 0 | |
add_blue(text_boxes) | |
# layout8(text_boxes) | |
# Our big_box is very wide now, that is why we have long lines. Let's make it narrower | |
big_box = Box(0, 0, 30, 1000) | |
# layout8(text_boxes) | |
# But our right side is ragged again! We should make spaces red. | |
def add_red(boxes): | |
for b in boxes: | |
if b.letter == ' ': | |
b.red = True | |
add_red(text_boxes) | |
# layout8(text_boxes) | |
# The second paragraph of Chapter 1 shows a red space as first thing in the row, and that looks bad! | |
# So, when the 1st letter in a row is a space, let's make it take no width and not stretch | |
def layout9(boxes): | |
last_break = 0 | |
for i, box in enumerate(boxes[1:], 1): | |
prev_box = boxes[i-1] | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or box.x > big_box.w: | |
box.x = 0 | |
if box.red: | |
box.w = 0 | |
if prev_box.blue: | |
box.y = prev_box.y + 2.1 | |
else: # not blue | |
box.y = prev_box.y + 1.1 | |
row = boxes[last_break:i] | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
last_break = i | |
# Just for fun, let's draw it without the colored boxes | |
# layout9(text_boxes) | |
# draw_boxes(text_boxes, False) | |
# XXX layout9 rewritten using pop(), need to backport this version | |
def layout10(_boxes): | |
boxes = _boxes[:] # Work on a copy | |
prev_box = boxes.pop(0) | |
row = [prev_box] | |
while(boxes): | |
box = boxes.pop(0) | |
row.append(box) | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or box.x > big_box.w: | |
box.x = 0 | |
row.pop() # our box will go in the next row | |
if box.red: | |
box.w = 0 | |
if prev_box.blue: | |
box.y = prev_box.y + 2.1 | |
else: # not blue | |
box.y = prev_box.y + 1.1 | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
row = [box] | |
prev_box = box | |
# layout10(text_boxes) | |
# Looks good, except that the words are broken wrong. You can't break good like: g | |
# ood! | |
# What if we only break on spaces? | |
def layout11(_boxes): | |
boxes = _boxes[:] # Work on a copy | |
prev_box = boxes.pop(0) | |
row = [prev_box] | |
while(boxes): | |
box = boxes.pop(0) | |
row.append(box) | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or (box.x > big_box.w and box.red) : | |
row.pop() # our box will go in the next row | |
box.x = 0 | |
if box.red: | |
box.w = 0 | |
if prev_box.blue: | |
box.y = prev_box.y + 2.1 | |
else: # not blue | |
box.y = prev_box.y + 1.1 | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
row = [box] | |
prev_box = box | |
# layout11(text_boxes) | |
# That actually ... worked? Except that when we need to fit something wider than | |
# big_box because we did not break the row, the slack is NEGATIVE and words get | |
# smushed together! | |
# What we actually need is hyphenation. | |
# We can use pyphen to insert soft-hyphen characters wherever words can break. | |
# And we can mark those positions yellow. | |
import pyphen | |
hyphenator = pyphen.Pyphen(lang='en_GB') # These things are language dependent | |
p_and_p = open('pride-and-prejudice.txt').readlines() | |
for i, l in enumerate(p_and_p): | |
words = l.split(' ') | |
p_and_p[i] = ' '.join(hyphenator.inserted(w, '\u00AD') for w in words) | |
p_and_p = ''.join(p_and_p) | |
text_boxes = [] | |
for l in p_and_p: | |
text_boxes.append(Box(letter=l)) | |
# This makes the characters '\u00AD' (soft-hyphen) yellow. | |
def add_yellow(boxes): | |
for b in boxes: | |
if b.letter == '\u00AD': | |
b.yellow = True | |
add_blue(text_boxes) | |
add_red(text_boxes) | |
add_yellow(text_boxes) | |
adjust_widths_by_letter(text_boxes) | |
# And create a new layout function that also breaks on yellow boxes. | |
def layout12(_boxes): | |
boxes = _boxes[:] # Work on a copy | |
prev_box = boxes.pop(0) | |
row = [prev_box] | |
while(boxes): | |
box = boxes.pop(0) | |
row.append(box) | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or (box.x > big_box.w and (box.red or box.yellow)) : | |
row.pop() # our box will go in the next row | |
box.x = 0 | |
if box.red: | |
box.w = 0 | |
if prev_box.blue: | |
box.y = prev_box.y + 2.1 | |
else: # not blue | |
box.y = prev_box.y + 1.1 | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
row = [box] | |
prev_box = box | |
# layout12(text_boxes) | |
# Better! Since we have more break chances, there is less word-smushing. | |
# However our typography is wrong, because we are hyphenating but not showing a hyphen! | |
# So, we need to ADD a box when we hyphenate. That special box is hyphenbox(): | |
def hyphenbox(): # Yes, this is not optimal | |
b = Box(letter='-', yellow=True) | |
adjust_widths_by_letter([b]) | |
return b | |
def layout13(_boxes): | |
boxes = _boxes[:] # Work on a copy | |
prev_box = boxes.pop(0) | |
row = [prev_box] | |
while(boxes): | |
box = boxes.pop(0) | |
row.append(box) | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue or (box.x > big_box.w and (box.red or box.yellow)): | |
row.pop() # our box will go in the next row | |
if box.yellow: # We need to insert the hyphen! | |
h_b = hyphenbox() | |
h_b.x = prev_box.x + prev_box.w + separation | |
h_b.y = prev_box.y | |
_boxes.append(h_b) # So it's drawn | |
row.append(h_b) # So it's justified | |
box.x = 0 | |
if box.red: | |
box.w = 0 | |
if prev_box.blue: | |
box.y = prev_box.y + 2.1 | |
else: # not blue | |
box.y = prev_box.y + 1.1 | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
row = [box] | |
prev_box = box | |
# layout13(text_boxes) | |
# Good. However, we still have smushing. It's usually considered better to make spaces | |
# between words grow, rather than shrink. So, what we should do is, instead of breaking | |
# in the 1st yellow/red PAST the break, go back and break in the last BEFORE the break! | |
# XXX backport simplifications from layout14: separation of blue case | |
def layout14(_boxes): | |
boxes = _boxes[:] # Work on a copy | |
row = [boxes.pop(0)] | |
while(boxes): | |
prev_box = row[-1] | |
box = boxes.pop(0) | |
row.append(box) | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue: | |
box.x = 0 | |
box.y = prev_box.y + 2.1 | |
row.pop() | |
row=[box] | |
elif box.x > big_box.w: | |
while not (box.red or box.yellow): # backtrack onw | |
boxes.insert(0, row.pop()) | |
# tip of row is now box, previous one is prev_box | |
box = row[-1] | |
prev_box = row[-2] | |
row.pop() # Breaking box goes in next row | |
if box.yellow: # We need to insert the hyphen! | |
h_b = hyphenbox() | |
h_b.x = prev_box.x + prev_box.w + separation | |
h_b.y = prev_box.y | |
_boxes.append(h_b) # So it's drawn | |
row.append(h_b) # So it's justified | |
box.x = 0 | |
box.y = prev_box.y + 1.1 | |
if box.red: | |
box.w = 0 | |
slack = big_box.w - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
row = [box] | |
# With a little separation for niceness, the result is not half bad! | |
separation = 0.05 | |
# layout14(text_boxes) | |
# So far, our big_box has been very, very tall. What would happen if it were short? | |
big_box = Box(0, 0, 30, 60) | |
# layout14(text_boxes) | |
# Well, that our text flows straight out of the box! And our initial problem was | |
# "So, how could we layout the many boxes inside the big_box?" ! | |
# That won't do. Clearly, if we have more text than fits in a box, we need more than one | |
# box. Like pages. | |
# So, let's make many big boxes. | |
pages = [Box(0, i * 65, 30, 60) for i in range(50)] | |
# layout14(text_boxes) | |
# Of course our layout engine does nothing with those pages. Let's fix that. | |
def layout15(_boxes): | |
boxes = _boxes[:] # Work on a copy | |
row = [boxes.pop(0)] | |
# Put the 1st box in the beginning of the 1st page | |
page = 0 | |
row[0].x = pages[page].x | |
row[0].y = pages[page].y | |
while(boxes): | |
prev_box = row[-1] | |
box = boxes.pop(0) | |
row.append(box) | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue: | |
box.x = 0 | |
box.y = prev_box.y + 2.1 | |
row.pop() | |
row=[box] | |
elif box.x > pages[page].w: | |
while not (box.red or box.yellow): # backtrack onw | |
boxes.insert(0, row.pop()) | |
# tip of row is now box, previous one is prev_box | |
box = row[-1] | |
prev_box = row[-2] | |
row.pop() # Breaking box goes in next row | |
if box.yellow: # We need to insert the hyphen! | |
h_b = hyphenbox() | |
h_b.x = prev_box.x + prev_box.w + separation | |
h_b.y = prev_box.y | |
_boxes.append(h_b) # So it's drawn | |
row.append(h_b) # So it's justified | |
box.x = 0 | |
box.y = prev_box.y + 1.1 | |
if box.red: | |
box.w = 0 | |
slack = pages[page].w - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
row = [box] | |
if box.y + box.h > pages[page].y + pages[page].h: | |
# We need to go to the next page | |
page = page + 1 | |
box.y = pages[page].y | |
# layout15(text_boxes) | |
# And there you go, pagination | |
# What happens if pages are organized differently? | |
# Let's put them left-to-right | |
pages = [Box(i*35, 0, 30, 60) for i in range(50)] | |
# layout15(text_boxes) | |
# Everything is in the same page, because we are resetting x to 0 on line breaks. | |
# You always need to find your hidden assumptions. | |
def layout16(_boxes): | |
boxes = _boxes[:] # Work on a copy | |
row = [boxes.pop(0)] | |
# Put the 1st box in the beginning of the 1st page | |
page = 0 | |
row[0].x = pages[page].x | |
row[0].y = pages[page].y | |
while(boxes): | |
prev_box = row[-1] | |
box = boxes.pop(0) | |
row.append(box) | |
box.x = prev_box.x + prev_box.w + separation | |
box.y = prev_box.y | |
if prev_box.blue: | |
box.x = pages[page].x | |
box.y = prev_box.y + 2.1 | |
row.pop() | |
row=[box] | |
elif box.x > pages[page].w + pages[page].x: | |
while not (box.red or box.yellow): # backtrack onw | |
boxes.insert(0, row.pop()) | |
# tip of row is now box, previous one is prev_box | |
box = row[-1] | |
prev_box = row[-2] | |
row.pop() # Breaking box goes in next row | |
if box.yellow: # We need to insert the hyphen! | |
h_b = hyphenbox() | |
h_b.x = prev_box.x + prev_box.w + separation | |
h_b.y = prev_box.y | |
_boxes.append(h_b) # So it's drawn | |
row.append(h_b) # So it's justified | |
box.x = pages[page].x | |
box.y = prev_box.y + 1.1 | |
if box.red: | |
box.w = 0 | |
slack = (pages[page].w + pages[page].x) - (row[-1].x + row[-1].w) | |
# If the 1st thing is a red, that one doesn't stretch | |
reds = [b for b in row[1:] if b.red] | |
# sometimes there is no red in the row. Do nothing. | |
if reds: | |
mini_slack = slack / len(reds) | |
for b in reds: | |
b.w += mini_slack | |
for j, b in enumerate(row[1:], 1): | |
b.x = row[j-1].x + row[j-1].w + separation | |
row = [box] | |
# We may need to go to the next page | |
if box.y + box.h > pages[page].y + pages[page].h: | |
page = page + 1 | |
# Since the location of a page is arbitrary, both coordinates are reset | |
box.y = pages[page].y | |
box.x = pages[page].x | |
# This may even work with oddly-sized pages! | |
pages = [Box(i*35, 0, 20, 60 + random.randint(-20, 20)) for i in range(50)] | |
layout16(text_boxes) | |
draw_boxes(text_boxes, False) | |
# This is a good point to STOP. Look at the code, and clean house. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment