aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Vilcans <martin@librador.com>2014-10-07 22:50:50 +0200
committerMartin Vilcans <martin@librador.com>2014-10-09 09:34:27 +0200
commit8f9e7ded799c173e3bddd971f337e2895304c944 (patch)
tree0364ace718614d98b40f5a0726f87a0517f425bb
parent8ee8e6158284e2c3f776c814c62fe17b4eebc969 (diff)
downloadscreenplain-8f9e7ded799c173e3bddd971f337e2895304c944.tar.gz
First stab at PDF output
-rw-r--r--requirements.txt2
-rw-r--r--screenplain/export/pdf.py138
-rw-r--r--screenplain/main.py5
-rwxr-xr-xsetup.py3
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,
diff --git a/setup.py b/setup.py
index 8261a7f..6165ef5 100755
--- a/setup.py
+++ b/setup.py
@@ -9,6 +9,9 @@ setup(
author='Martin Vilcans',
author_email='screenplain@librador.com',
url='http://www.screenplain.com/',
+ requires=[
+ 'reportlab',
+ ],
packages=[
'screenplain',
'screenplain.export',