diff options
-rw-r--r-- | screenplain/export/fdx.py | 80 | ||||
-rw-r--r-- | screenplain/main.py | 8 | ||||
-rw-r--r-- | tests/fdx_test.py | 45 |
3 files changed, 132 insertions, 1 deletions
diff --git a/screenplain/export/fdx.py b/screenplain/export/fdx.py new file mode 100644 index 0000000..32e18f5 --- /dev/null +++ b/screenplain/export/fdx.py @@ -0,0 +1,80 @@ +from xml.sax.saxutils import escape + +from screenplain.types import * +from screenplain.richstring import RichString, Bold, Italic, Underline + +paragraph_types = { + Slug: 'Scene Heading', + Action: 'Action', + Transition: 'Transition', +} + +style_names = { + Bold: 'Bold', + Italic: 'Italic', + Underline: 'Underline', + RichString: None, +} + + +def _write_text_element(out, styles, text): + style_value = '+'.join(str(s) for s in styles) + if style_value == '': + out.write('<Text>%s</Text>' % (escape(text))) + else: + out.write('<Text style="%s">%s</Text>' % (style_value, escape(text))) + + +def write_text(out, rich, parent_styles=None): + parent_styles = parent_styles or set() + style_name = style_names[type(rich)] + if style_name: + styles = parent_styles | set((style_name,)) + else: + styles = parent_styles + + for segment in rich.segments: + if isinstance(segment, basestring): + _write_text_element(out, styles, segment) + else: + write_text(out, segment, styles) + + +def write_paragraph(out, para_type, lines): + out.write('<Paragraph Type="%s">' % para_type) + for line in lines: + write_text(out, line) + out.write('</Paragraph>') + + +def write_dialog(out, dialog): + write_paragraph(out, 'Character', [dialog.character]) + for parenthetical, line in dialog.blocks: + if parenthetical: + write_paragraph(out, 'Parenthetical', [line]) + else: + write_paragraph(out, 'Dialogue', [line]) + +def write_dual_dialog(out, dual): + out.write('<Paragraph><DualDialogue>') + write_dialog(out, dual.left) + write_dialog(out, dual.right) + out.write('</DualDialogue></Paragraph>') + +def to_fdx(screenplay, out): + + out.write( + '<?xml version="1.0" encoding="UTF-8" standalone="no" ?>' + '<FinalDraft DocumentType="Script" Template="No" Version="1">' + '<Content>' + ) + + for para in screenplay: + if isinstance(para, Dialog): + write_dialog(out, para) + elif isinstance(para, DualDialog): + write_dual_dialog(out, para) + else: + para_type = paragraph_types[type(para)] + write_paragraph(out, para_type, para.lines) + out.write('</Content></FinalDraft>\n') diff --git a/screenplain/main.py b/screenplain/main.py index 2c114a6..43b48d5 100644 --- a/screenplain/main.py +++ b/screenplain/main.py @@ -11,7 +11,7 @@ from optparse import OptionParser from screenplain.parsers.spmd import parse output_formats = ( - 'text', 'pdf', 'annotated_html' + 'text', 'pdf', 'fdx', 'annotated_html' ) usage = 'Usage: %prog [options] input-file output-file' @@ -33,8 +33,11 @@ def main(args): if options.output_format == None: if output_file.endswith('.pdf'): options.output_format = 'pdf' + elif output_file.endswith('.fdx'): + options.output_format = 'fdx' else: options.output_format = 'text' + if options.output_format not in output_formats: parser.error( 'Unknown output format. Expected one of: ' + @@ -52,6 +55,9 @@ def main(args): if options.output_format == 'text': from screenplain.export.text import to_text to_text(screenplay, output) + elif options.output_format == 'fdx': + from screenplain.export.fdx import to_fdx + to_fdx(screenplay, output) elif options.output_format == 'annotated_html': from screenplain.export.annotated_html import to_annotated_html to_annotated_html(screenplay, output) diff --git a/tests/fdx_test.py b/tests/fdx_test.py new file mode 100644 index 0000000..9737493 --- /dev/null +++ b/tests/fdx_test.py @@ -0,0 +1,45 @@ +import unittest2 +from StringIO import StringIO + +from screenplain.export.fdx import write_text + +from screenplain.richstring import RichString, Bold, Italic + +class OutputTests(unittest2.TestCase): + + def setUp(self): + self.out = StringIO() + + def test_plain_text_should_have_no_style(self): + write_text(self.out, RichString('hello')) + self.assertEqual( + self.out.getvalue(), + '<Text>hello</Text>' + ) + + def test_bold_text_should_have_bold_style(self): + write_text(self.out, Bold('hello')) + self.assertEqual( + self.out.getvalue(), + '<Text style="Bold">hello</Text>' + ) + + def test_sequential_styles(self): + rich = RichString('plain', Bold('b'), Italic('i')) + write_text(self.out, rich) + self.assertEqual( + self.out.getvalue(), + '<Text>plain</Text>' + '<Text style="Bold">b</Text>' + '<Text style="Italic">i</Text>' + ) + + def test_nested_styles(self): + rich = Bold('outer', Italic('inner'), 'outer') + write_text(self.out, rich) + self.assertEqual( + self.out.getvalue(), + '<Text style="Bold">outer</Text>' + '<Text style="Bold+Italic">inner</Text>' + '<Text style="Bold">outer</Text>' + ) |