# HG changeset patch # User Ludovic Chabant # Date 1483527368 28800 # Node ID a5488b474c6b23f843f123452e8d7691a8b15ad4 # Parent 02d2e4d8b0c1e53d24129e467dfdbb8abc72051e Add HTML renderer. diff -r 02d2e4d8b0c1 -r a5488b474c6b .hgignore --- a/.hgignore Wed Jan 04 02:55:20 2017 -0800 +++ b/.hgignore Wed Jan 04 02:56:08 2017 -0800 @@ -2,4 +2,6 @@ __pycache__ venv .cache +.sass-cache +*.css.map *.pyc diff -r 02d2e4d8b0c1 -r a5488b474c6b fontaine/html.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fontaine/html.py Wed Jan 04 02:56:08 2017 -0800 @@ -0,0 +1,110 @@ +import os.path +from markupsafe import escape +from .renderer import BaseDocumentRenderer, BaseTextRenderer + + +def _elem(out, elem_name, class_name, contents): + f = out.write + f('<%s' % elem_name) + if class_name: + f(' class="fontaine-%s"' % class_name) + f('>') + f(contents) + f('\n' % elem_name) + + +def _br(text, strip_first=False): + lines = text.split('\n') + if strip_first and lines[0].strip() == '': + lines = lines[1:] + return '
\n'.join(lines) + + +def _res(filename): + path = os.path.join(os.path.dirname(__file__), 'resources', filename) + with open(path, 'r') as fp: + return fp.read() + + +class HtmlDocumentRenderer(BaseDocumentRenderer): + def __init__(self, standalone=True): + super().__init__(HtmlTextRenderer()) + self.standalone = standalone + + def get_css(self): + return _res('html_styles.css') + + def write_header(self, doc, out): + if self.standalone: + meta = doc.title_values.get + data = { + # TODO: need a "strip formatting" to have a clean title. + 'title': meta('title', "Fountain Screenplay"), + 'description': meta('description', ''), + 'css': self.get_css() + } + out.write(_res('html_header.html') % data) + out.write('
\n') + + def write_footer(self, doc, out): + out.write('
\n') + if self.standalone: + out.write(_res('html_footer.html')) + + def write_title_page(self, values, out): + out.write('
\n') + + _elem(out, 'h1', None, _br(values['title'])) + _elem(out, 'p', 'title-page-heading', _br(values['credit'])) + _elem(out, 'p', 'title-page-heading', _br(values['author'])) + + ddate = values.get('date') or values.get('draft date') + if ddate: + _elem(out, 'p', 'title-page-footer', _br(ddate)) + contact = values.get('contact') + if contact: + _elem(out, 'p', 'title-page-footer', _br(contact)) + + out.write('
\n') + self.write_pagebreak(out) + + def write_scene_heading(self, text, out): + _elem(out, 'p', 'scene-heading', text) + + def write_action(self, text, out): + _elem(out, 'p', 'action', _br(text, True)) + + def write_centeredaction(self, text, out): + _elem(out, 'p', 'action-centered', _br(text, True)) + + def write_character(self, text, out): + _elem(out, 'p', 'character', text) + + def write_dialog(self, text, out): + _elem(out, 'p', 'dialog', _br(text)) + + def write_parenthetical(self, text, out): + _elem(out, 'p', 'parenthetical', text) + + def write_transition(self, text, out): + _elem(out, 'p', 'transition', text) + + def write_lyrics(self, text, out): + _elem(out, 'p', 'lyrics', _br(text, True)) + + def write_pagebreak(self, out): + out.write('
\n') + + +class HtmlTextRenderer(BaseTextRenderer): + def render_text(self, text): + return super().render_text(escape(text)) + + def make_italics(self, text): + return '%s' % text + + def make_bold(self, text): + return '%s' % text + + def make_underline(self, text): + return '%s' % text diff -r 02d2e4d8b0c1 -r a5488b474c6b fontaine/resources/html_footer.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fontaine/resources/html_footer.html Wed Jan 04 02:56:08 2017 -0800 @@ -0,0 +1,3 @@ + + + diff -r 02d2e4d8b0c1 -r a5488b474c6b fontaine/resources/html_header.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fontaine/resources/html_header.html Wed Jan 04 02:56:08 2017 -0800 @@ -0,0 +1,40 @@ + + + + + + %(title)s + + + + + + + + + + diff -r 02d2e4d8b0c1 -r a5488b474c6b fontaine/resources/html_styles.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fontaine/resources/html_styles.css Wed Jan 04 02:56:08 2017 -0800 @@ -0,0 +1,16 @@ +.fontaine-doc { + font-family: "Courier Prime", "Courier New", Courier, sans-serif; } + .fontaine-doc h1, .fontaine-doc .fontaine-title-page-heading { + text-align: center; } + .fontaine-doc .fontaine-scene-heading { + font-weight: bold; } + .fontaine-doc .fontaine-character { + margin: auto 12rem 0 12rem; } + .fontaine-doc .fontaine-parenthetical { + margin: 0 8rem 0 8rem; } + .fontaine-doc .fontaine-dialog { + margin: 0 4rem 0 4rem; } + .fontaine-doc .fontaine-action-centered { + text-align: center; } + +/*# sourceMappingURL=html_styles.css.map */ diff -r 02d2e4d8b0c1 -r a5488b474c6b fontaine/resources/html_styles.scss --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fontaine/resources/html_styles.scss Wed Jan 04 02:56:08 2017 -0800 @@ -0,0 +1,30 @@ +.fontaine-doc { + font-family: "Courier Prime", "Courier New", Courier, sans-serif; + + h1, .fontaine-title-page-heading { + text-align: center; + } + + .fontaine-scene-heading { + font-weight: bold; + } + + .fontaine-transition { + } + + .fontaine-character { + margin: auto 3 * 4rem 0 3 * 4rem; + } + + .fontaine-parenthetical { + margin: 0 2 * 4rem 0 2 * 4rem; + } + + .fontaine-dialog { + margin: 0 4rem 0 4rem; + } + + .fontaine-action-centered { + text-align: center; + } +} diff -r 02d2e4d8b0c1 -r a5488b474c6b scripts/fontaine --- a/scripts/fontaine Wed Jan 04 02:55:20 2017 -0800 +++ b/scripts/fontaine Wed Jan 04 02:56:08 2017 -0800 @@ -11,15 +11,22 @@ parser = argparse.ArgumentParser( description='Fontaine command line utility') parser.add_argument('script') + parser.add_argument('out_file', nargs='?') args = parser.parse_args() from fontaine.parser import FontaineParser - from fontaine.console import ConsoleDocumentRenderer p = FontaineParser() doc = p.parse(args.script) - rdr = ConsoleDocumentRenderer() - rdr.render_doc(doc) + if not args.out_file: + from fontaine.console import ConsoleDocumentRenderer + rdr = ConsoleDocumentRenderer() + rdr.render_doc(doc, sys.stdout) + else: + from fontaine.html import HtmlDocumentRenderer + rdr = HtmlDocumentRenderer() + with open(args.out_file, 'w') as fp: + rdr.render_doc(doc, fp) if __name__ == '__main__': main()