diff options
| -rw-r--r-- | screenplain/export/pdf.py | 2 | ||||
| -rw-r--r-- | screenplain/export/text.py | 2 | ||||
| -rw-r--r-- | screenplain/format.py | 19 | ||||
| -rw-r--r-- | screenplain/main.py | 2 | ||||
| -rw-r--r-- | screenplain/parsers/__init__.py | 0 | ||||
| -rw-r--r-- | screenplain/parsers/spmd.py | 66 | ||||
| -rw-r--r-- | screenplain/types.py (renamed from screenplain/parse.py) | 84 | ||||
| -rw-r--r-- | tests/spmd_test.py (renamed from tests/parse_test.py) | 30 |
8 files changed, 103 insertions, 102 deletions
diff --git a/screenplain/export/pdf.py b/screenplain/export/pdf.py index 48eeab8..f7c9f24 100644 --- a/screenplain/export/pdf.py +++ b/screenplain/export/pdf.py @@ -3,7 +3,7 @@ import fileinput from reportlab.pdfgen import canvas from reportlab.lib import pagesizes -from screenplain.parse import parse, get_pages +from screenplain.format import get_pages def to_pdf(screenplay, output_file): # pagesizes.letter, pagesizes.A4 diff --git a/screenplain/export/text.py b/screenplain/export/text.py index 8f7cb3d..12b8723 100644 --- a/screenplain/export/text.py +++ b/screenplain/export/text.py @@ -1,6 +1,6 @@ import sys import codecs -from screenplain.parse import parse, get_pages +from screenplain.format import get_pages def to_text(screenplay, output_file): out = codecs.open(output_file, 'w', 'utf-8') diff --git a/screenplain/format.py b/screenplain/format.py new file mode 100644 index 0000000..be5298b --- /dev/null +++ b/screenplain/format.py @@ -0,0 +1,19 @@ +# Numbers from http://www.emacswiki.org/emacs/ScreenPlay +# According to http://johnaugust.com/2004/how-many-lines-per-page +lines_per_page = 56 + + +def get_pages(paragraphs): + """Generates one list of lines per page.""" + lines_on_page = [] + for paragraph in paragraphs: + top_margin = paragraph.top_margin if lines_on_page else 0 + para_lines = list(paragraph.format()) + + if len(lines_on_page) + top_margin + len(para_lines) > lines_per_page: + yield lines_on_page + lines_on_page = [] + else: + lines_on_page += [''] * top_margin + lines_on_page += para_lines + yield lines_on_page diff --git a/screenplain/main.py b/screenplain/main.py index 79ff3eb..469da0f 100644 --- a/screenplain/main.py +++ b/screenplain/main.py @@ -8,7 +8,7 @@ import sys import codecs from optparse import OptionParser -from parse import parse +from screenplain.parsers.spmd import parse usage = 'Usage: %prog [options] input-file output-file' diff --git a/screenplain/parsers/__init__.py b/screenplain/parsers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/screenplain/parsers/__init__.py diff --git a/screenplain/parsers/spmd.py b/screenplain/parsers/spmd.py new file mode 100644 index 0000000..3da8864 --- /dev/null +++ b/screenplain/parsers/spmd.py @@ -0,0 +1,66 @@ +import itertools +from screenplain.types import Slug, Action, Dialog, DualDialog, Transition + +slug_prefixes = ( + 'INT. ', + 'EXT. ', + 'INT./EXT. ', + 'INT/EXT. ', + 'INT ', + 'EXT ', + 'INT/EXT ', + 'I/E ', +) + +TWOSPACE = ' ' * 2 + +def is_blank(string): + return string == '' or string.isspace() and string != ' ' + +def is_slug(blanks_before, string): + if blanks_before >= 2: + return True + upper = string.upper() + return any(upper.startswith(s) for s in slug_prefixes) + +def _create_dialog(line_list): + try: + dual_index = line_list.index('||') + except ValueError: + return Dialog(line_list) + else: + return DualDialog(line_list[:dual_index], line_list[dual_index + 1:]) + +def create_paragraph(blanks_before, line_list): + if is_slug(blanks_before, line_list[0]): + return Slug(line_list) + if ( + len(line_list) > 1 and + line_list[0].isupper() and + not line_list[0].endswith(TWOSPACE) + ): + return _create_dialog(line_list) + elif len(line_list) == 1 and line_list[0].endswith(':') and line_list[0].isupper(): + # TODO: need to check whether the *next* paragraph is a slug + # before assuming this is a transition. + return Transition(line_list) + else: + return Action(line_list) + +def clean_line(line): + """Strips leading whitespace and trailing end of line characters in a string. + + Leading whitespace is insignificant in SPMD, and trailing EOL + appear when reading from a file or HTML form. + """ + return line.lstrip().rstrip('\r\n') + +def parse(source): + """Reads raw text input and generates paragraph objects.""" + blank_count = 0 + source = (clean_line(line) for line in source) + for blank, lines in itertools.groupby(source, is_blank): + if blank: + blank_count = len(list(lines)) + else: + yield create_paragraph(blank_count, list(lines)) diff --git a/screenplain/parse.py b/screenplain/types.py index 9e97eed..4b12418 100644 --- a/screenplain/parse.py +++ b/screenplain/types.py @@ -1,23 +1,4 @@ -import itertools import textwrap -import re - -# Numbers from http://www.emacswiki.org/emacs/ScreenPlay -# According to http://johnaugust.com/2004/how-many-lines-per-page -lines_per_page = 56 - -slug_prefixes = ( - 'INT. ', - 'EXT. ', - 'INT./EXT. ', - 'INT/EXT. ', - 'INT ', - 'EXT ', - 'INT/EXT ', - 'I/E ', -) - -TWOSPACE = ' ' * 2 class Slug(object): indent = '' @@ -115,68 +96,3 @@ class Transition(object): for line in textwrap.wrap(self.text, width=self.fill): yield self.indent + line -def is_blank(string): - return string == '' or string.isspace() and string != ' ' - -def is_slug(blanks_before, string): - if blanks_before >= 2: - return True - upper = string.upper() - return any(upper.startswith(s) for s in slug_prefixes) - -def _create_dialog(line_list): - try: - dual_index = line_list.index('||') - except ValueError: - return Dialog(line_list) - else: - return DualDialog(line_list[:dual_index], line_list[dual_index + 1:]) - -def create_paragraph(blanks_before, line_list): - if is_slug(blanks_before, line_list[0]): - return Slug(line_list) - if ( - len(line_list) > 1 and - line_list[0].isupper() and - not line_list[0].endswith(TWOSPACE) - ): - return _create_dialog(line_list) - elif len(line_list) == 1 and line_list[0].endswith(':') and line_list[0].isupper(): - # TODO: need to check whether the *next* paragraph is a slug - # before assuming this is a transition. - return Transition(line_list) - else: - return Action(line_list) - -def clean_line(line): - """Strips leading whitespace and trailing end of line characters in a string. - - Leading whitespace is insignificant in SPMD, and trailing EOL - appear when reading from a file or HTML form. - """ - return line.lstrip().rstrip('\r\n') - -def parse(source): - """Reads raw text input and generates paragraph objects.""" - blank_count = 0 - source = (clean_line(line) for line in source) - for blank, lines in itertools.groupby(source, is_blank): - if blank: - blank_count = len(list(lines)) - else: - yield create_paragraph(blank_count, list(lines)) - -def get_pages(paragraphs): - """Generates one list of lines per page.""" - lines_on_page = [] - for paragraph in paragraphs: - top_margin = paragraph.top_margin if lines_on_page else 0 - para_lines = list(paragraph.format()) - - if len(lines_on_page) + top_margin + len(para_lines) > lines_per_page: - yield lines_on_page - lines_on_page = [] - else: - lines_on_page += [''] * top_margin - lines_on_page += para_lines - yield lines_on_page diff --git a/tests/parse_test.py b/tests/spmd_test.py index 1eabdb2..cb349a1 100644 --- a/tests/parse_test.py +++ b/tests/spmd_test.py @@ -1,6 +1,6 @@ import unittest2 -from screenplain import parse -from screenplain.parse import Slug, Action, Dialog, DualDialog, Transition +from screenplain.parsers.spmd import parse +from screenplain.types import Slug, Action, Dialog, DualDialog, Transition class ParseTests(unittest2.TestCase): @@ -14,7 +14,7 @@ class ParseTests(unittest2.TestCase): # least one blank line preceding it. # NOTE: Actually the list used in Appendix 1 def test_slug_with_prefix(self): - paras = list(parse.parse([ + paras = list(parse([ 'INT. SOMEWHERE - DAY', '', 'THIS IS JUST ACTION', @@ -22,14 +22,14 @@ class ParseTests(unittest2.TestCase): self.assertEquals([Slug, Action], [type(p) for p in paras]) def test_action_is_not_a_slug(self): - paras = list(parse.parse([ + paras = list(parse([ '', 'THIS IS JUST ACTION', ])) self.assertEquals([Action], [type(p) for p in paras]) def test_two_lines_creates_a_slug(self): - types = [type(p) for p in parse.parse([ + types = [type(p) for p in parse([ '', '', 'This is a slug', @@ -40,7 +40,7 @@ class ParseTests(unittest2.TestCase): # A Character element is any line entirely in caps, with one empty # line before it and without an empty line after it. def test_all_caps_is_character(self): - paras = [p for p in parse.parse([ + paras = [p for p in parse([ 'SOME GUY', 'Hello', ])] @@ -52,7 +52,7 @@ class ParseTests(unittest2.TestCase): # SPMD would not be able to support a character named "23". We # might need a syntax to force a character element. def test_nonalpha_character(self): - paras = list(parse.parse([ + paras = list(parse([ '23', 'Hello', ])) @@ -61,14 +61,14 @@ class ParseTests(unittest2.TestCase): # See # http://prolost.com/storage/downloads/spmd/SPMD_proposal.html#section-br def test_twospaced_line_is_not_character(self): - paras = list(parse.parse([ + paras = list(parse([ 'SCANNING THE AISLES... ', 'Where is that pit boss?', ])) self.assertEquals([Action], [type(p) for p in paras]) def test_simple_parenthetical(self): - paras = list(parse.parse([ + paras = list(parse([ 'STEEL', '(starting the engine)', 'So much for retirement!', @@ -80,7 +80,7 @@ class ParseTests(unittest2.TestCase): self.assertEqual((False, 'So much for retirement!'), dialog.blocks[1]) def test_dual_dialog(self): - paras = list(parse.parse([ + paras = list(parse([ 'BRICK', 'Fuck retirement.', '||', @@ -96,7 +96,7 @@ class ParseTests(unittest2.TestCase): def test_standard_transition(self): - paras = list(parse.parse([ + paras = list(parse([ 'Jack begins to argue vociferously in Vietnamese (?)', '', 'CUT TO:', @@ -107,7 +107,7 @@ class ParseTests(unittest2.TestCase): def test_standard_transition(self): - paras = list(parse.parse([ + paras = list(parse([ 'Jack begins to argue vociferously in Vietnamese (?)', '', 'CUT TO:', @@ -117,7 +117,7 @@ class ParseTests(unittest2.TestCase): self.assertEquals([Action, Transition, Slug], [type(p) for p in paras]) def test_transition_needs_to_be_upper_case(self): - paras = list(parse.parse([ + paras = list(parse([ 'Jack begins to argue vociferously in Vietnamese (?)', '', 'cut to:', @@ -127,7 +127,7 @@ class ParseTests(unittest2.TestCase): self.assertEquals([Action, Action, Slug], [type(p) for p in paras]) def test_not_a_transition_on_trailing_whitespace(self): - paras = list(parse.parse([ + paras = list(parse([ 'Jack begins to argue vociferously in Vietnamese (?)', '', 'CUT TO: ', @@ -139,7 +139,7 @@ class ParseTests(unittest2.TestCase): # Not implemented yet @unittest2.expectedFailure def test_transition_must_be_followed_by_slug(self): - paras = list(parse.parse([ + paras = list(parse([ 'Bill lights a cigarette.', '', 'CUT TO:', |
