BOXES v0.12

What we have right now is not a real program, it's just a fun script. In order to turn it into a real program we need to figure out what we want it to be. Then we will take our wish and make it happen. Easy, right?

We have the following things:

  • Code to generate text "boxes" representing a given text file.
  • Code to generate a series of pages.
  • Code to lay the text boxes in the pages.
  • Code to draw text boxes and pages in a SVG.

How can we combine those assets into a working, functional piece of software?

Because I am old my first idea is to create a command line tool. We can explore others later, but this one is easy. It takes the name of a text file as first argument, and the name of a SVG file as second argument and it creates the second with the contents of the first.

My favorite to do this is using docopt, where we will describe how the tool works and it turns the documentation into actual working code. It's serious overkill for what we want now, but we may grow into it.

We will start with an empty file and then add all the bits of code we have until it works.

Here's a first draft:

 3    boxes <input> <output>
 4    boxes --version
 7from docopt import docopt
10# __name__ is the name of the current module. If it's called as a
11# script, it will be '__main__'
12if __name__ == '__main__':
13    # If we are called as a script, call docopt.
14    # __doc__ is that big string at the beginning of the file.
15    arguments = docopt(__doc__, version='Boxes 0.12')
16    # Print whatever docopt gives us
17    print(arguments)

And here is how that works. If we call it with two arguments, we get the information in arguments:

1$ python foo bar
3{'--version': False,
4 '<input>': 'foo',
5 '<output>': 'bar'}

If we were missing one, then we are using it wrong, and it will complain and print the help.

1$ python foo
4    boxes <input> <output>
5    boxes --version

And if we pass --version?

1$ python foo
3Boxes 0.12

So, we have a dictionary with input and output as keys. That is handy. All we need to do is slap our existing code in this script and make it use those names. Because I don't want to show you a wall of code, I am going to just highlight some snippets. You can see the whole change in our diff page

Some of that code needs to be moved into proper functions:

201def create_text_boxes(input_file):
202    p_and_p = open(input_file).read()
203    p_and_p = insert_soft_hyphens(p_and_p)  # Insert invisible hyphens
204    text_boxes = []
205    for letter in p_and_p:
206        text_boxes.append(Box(letter=letter, stretchy=letter == " "))
207    adjust_widths_by_letter(text_boxes)
208    return text_boxes
211def create_pages():
212    # A few pages all the same size
213    pages = [Box(i * 35, 0, 30, 50) for i in range(10)]
214    return pages

We need to have pages be an argument to some functions instead of being a global variable:

63def layout(_boxes, pages):
164def draw_boxes(boxes, pages, fname, size, hide_boxes=False):

And of course, we need to write a new function that calls everything in the right order with the right arguments:

217def convert(input, output):
218    pages = create_pages()
219    text_boxes = create_text_boxes(input)
220    layout(text_boxes, pages)
221    draw_boxes(text_boxes, pages, output, ("100cm", "50cm"), True)
224if __name__ == "__main__":
225    arguments = docopt(__doc__, version="Boxes 0.12")
226    convert(input=arguments["<input>"], output=arguments["<output>"])

And if we run it like this:

$ python pride-and-prejudice.txt lesson1.svg

It will give us this output:


Further references:

results matching ""

    No results matching ""