diff options
-rw-r--r-- | requirements.txt | 2 | ||||
-rw-r--r-- | screenplain/export/pdf.py | 138 | ||||
-rw-r--r-- | screenplain/main.py | 5 | ||||
-rwxr-xr-x | setup.py | 3 |
4 files changed, 145 insertions, 3 deletions
diff --git a/requirements.txt b/requirements.txt index 3d09f64..321e747 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -#reportlab +reportlab unittest2 nose pep8 diff --git a/screenplain/export/pdf.py b/screenplain/export/pdf.py new file mode 100644 index 0000000..b43d801 --- /dev/null +++ b/screenplain/export/pdf.py @@ -0,0 +1,138 @@ +# Copyright (c) 2014 Martin Vilcans +# Licensed under the MIT license: +# http://www.opensource.org/licenses/mit-license.php + +from reportlab.lib import pagesizes +from reportlab.platypus import BaseDocTemplate, Paragraph, Frame, PageTemplate +from reportlab.lib.units import inch +from reportlab.lib.styles import ParagraphStyle + +from screenplain.types import ( + Action, Dialog, DualDialog, Transition, Slug +) + +lines_per_page = 55 +characters_per_line = 61 +frame_height = 12 * lines_per_page +frame_width = characters_per_line * 72 / 10.0 # Courier pitch is 10 chars/inch +page_width, page_height = pagesizes.letter +left_margin = 1.5 * inch +right_margin = page_width - left_margin - frame_width +top_margin = 1 * inch +bottom_margin = page_height - top_margin - frame_height + +character_width = 1.0 / 10 * inch + +default_style = ParagraphStyle( + 'default', + fontName='Courier', + fontSize=12, + leading=12, + spaceBefore=0, + spaceAfter=0, + leftIndent=0, + rightIndent=0, +) +character_style = ParagraphStyle( + 'character', default_style, + spaceBefore=12, + leftIndent=19 * character_width, + keepWithNext=1, +) +dialog_style = ParagraphStyle( + 'dialog', default_style, + leftIndent=9 * character_width, + rightIndent=frame_width - (45 * character_width), +) +parenthentical_style = ParagraphStyle( + 'parenthentical', default_style, + leftIndent=13 * character_width +) +action_style = ParagraphStyle( + 'action', default_style, + spaceBefore=12, +) +slug_style = ParagraphStyle( + 'slug', default_style, + spaceBefore=12, + spaceAfter=12, + keepWithNext=1, +) +transition_style = ParagraphStyle( + 'transition', default_style, + spaceBefore=12, + spaceAfter=12, +) + + +class DocTemplate(BaseDocTemplate): + def __init__(self, *args, **kwargs): + frame = Frame( + left_margin, bottom_margin, frame_width, frame_height, + id='normal', + leftPadding=0, topPadding=0, rightPadding=0, bottomPadding=0 + ) + pageTemplates = [ + PageTemplate(id='standard', frames=[frame]) + ] + BaseDocTemplate.__init__( + self, pageTemplates=pageTemplates, *args, **kwargs + ) + + def handle_pageBegin(self): + self.canv.setFont('Courier', 12, leading=12) + page = self.page + 1 + if page >= 2: + self.canv.drawRightString( + left_margin + frame_width, + page_height - 42, + '%s.' % page + ) + self._handle_pageBegin() + + +def add_paragraph(story, para, style): + for line in para.lines: + story.append(Paragraph(line.to_html(), style)) + + +def add_dialog(story, dialog): + story.append(Paragraph(dialog.character.to_html(), character_style)) + for parenthetical, line in dialog.blocks: + if parenthetical: + story.append(Paragraph(line.to_html(), parenthentical_style)) + else: + story.append(Paragraph(line.to_html(), dialog_style)) + + +def add_dual_dialog(story, dual): + # TODO: format dual dialog + add_dialog(story, dual.left) + add_dialog(story, dual.right) + + +def to_pdf(screenplay, output_filename, template_constructor=DocTemplate): + doc = template_constructor( + output_filename, + pagesize=(page_width, page_height), + #showBoundary=True, + ) + story = [] + for para in screenplay: + if isinstance(para, Dialog): + add_dialog(story, para) + elif isinstance(para, DualDialog): + add_dual_dialog(story, para) + elif isinstance(para, Action): + # TODO: heed para.centered + add_paragraph(story, para, action_style) + elif isinstance(para, Slug): + add_paragraph(story, para, slug_style) + elif isinstance(para, Transition): + add_paragraph(story, para, transition_style) + #elif isinstance(para, PageBreak): + # TODO: page break + else: + # Ignore unknown types + pass + doc.build(story) diff --git a/screenplain/main.py b/screenplain/main.py index 6def5af..ac308b5 100644 --- a/screenplain/main.py +++ b/screenplain/main.py @@ -4,7 +4,6 @@ # Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.php -import fileinput import sys import codecs from optparse import OptionParser @@ -12,7 +11,7 @@ from optparse import OptionParser from screenplain.parsers import fountain output_formats = ( - 'fdx', 'html' + 'fdx', 'html', 'pdf' ) usage = """Usage: %prog [options] [input-file [output-file]] @@ -62,6 +61,8 @@ def main(args): format = 'fdx' elif output_file.endswith('.html'): format = 'html' + elif output_file.endswith('.pdf'): + format = 'pdf' else: invalid_format( parser, @@ -9,6 +9,9 @@ setup( author='Martin Vilcans', author_email='screenplain@librador.com', url='http://www.screenplain.com/', + requires=[ + 'reportlab', + ], packages=[ 'screenplain', 'screenplain.export', |