aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--requirements.txt2
-rw-r--r--screenplain/export/pdf.py152
-rw-r--r--screenplain/main.py5
-rwxr-xr-xsetup.py3
5 files changed, 159 insertions, 4 deletions
diff --git a/.travis.yml b/.travis.yml
index b4da1b0..b90b600 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
language: python
python:
- "2.7"
- - "2.6"
install: pip install -r requirements.txt
script: bin/test
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..b5f0fb0
--- /dev/null
+++ b/screenplain/export/pdf.py
@@ -0,0 +1,152 @@
+# 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 import platypus
+from reportlab.lib.units import inch
+from reportlab.lib.styles import ParagraphStyle
+from reportlab.lib.enums import TA_CENTER
+
+from screenplain.types import (
+ Action, Dialog, DualDialog, Transition, Slug
+)
+from screenplain import types
+
+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,
+ keepWithNext=1,
+)
+action_style = ParagraphStyle(
+ 'action', default_style,
+ spaceBefore=12,
+)
+centered_action_style = ParagraphStyle(
+ 'centered-action', action_style,
+ alignment=TA_CENTER,
+)
+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),
+ )
+ 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):
+ add_paragraph(
+ story, para,
+ centered_action_style if para.centered else 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, types.PageBreak):
+ story.append(platypus.PageBreak())
+ 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',