changeset 9:a5488b474c6b

Add HTML renderer.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 04 Jan 2017 02:56:08 -0800
parents 02d2e4d8b0c1
children 2cea36073188
files .hgignore fontaine/html.py fontaine/resources/html_footer.html fontaine/resources/html_header.html fontaine/resources/html_styles.css fontaine/resources/html_styles.scss scripts/fontaine
diffstat 7 files changed, 211 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- 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
--- /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('</%s>\n' % elem_name)
+
+
+def _br(text, strip_first=False):
+    lines = text.split('\n')
+    if strip_first and lines[0].strip() == '':
+        lines = lines[1:]
+    return '<br/>\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('<div class="fontaine-doc">\n')
+
+    def write_footer(self, doc, out):
+        out.write('</div>\n')
+        if self.standalone:
+            out.write(_res('html_footer.html'))
+
+    def write_title_page(self, values, out):
+        out.write('<div class="fontaine-title-page">\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('</div>\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('<hr/>\n')
+
+
+class HtmlTextRenderer(BaseTextRenderer):
+    def render_text(self, text):
+        return super().render_text(escape(text))
+
+    def make_italics(self, text):
+        return '<em>%s</em>' % text
+
+    def make_bold(self, text):
+        return '<strong>%s</strong>' % text
+
+    def make_underline(self, text):
+        return '<u>%s</u>' % text
--- /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 @@
+        <p class="footer">Generated with <a href="https://bolt80.com/fontaine">Fontaine</a></p>
+    </body>
+</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 @@
+<!doctype html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta http-equiv="x-ua-compatible" content="ie=edge">
+        <title>%(title)s</title>
+        <meta name="description" content="%(description)s">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+
+        <link rel="apple-touch-icon" href="apple-touch-icon.png">
+        <!-- Place favicon.ico in the root directory -->
+
+        <style>
+        body {
+            margin: 1em 3em;
+            background-color: #666;
+        }
+        .fontaine-doc {
+            background-color: #fff;
+            padding: 2em;
+            box-shadow: #111 0px 0.5em 2em;
+        }
+        p.footer {
+            text-align: center;
+            text-transform: uppercase;
+            font-size: 0.9em;
+            font-family: "Times New Roman", "Times", serif;
+            color: #333;
+            padding: 1em;
+        }
+        p.footer a:link, p.footer a:visited, p.footer a:active {
+            color: #222;
+        }
+        p.footer a:hover {
+            color: #227;
+        }
+        </style>
+        <style>%(css)s</style>
+    </head>
+    <body>
--- /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 */
--- /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;
+    }
+}
--- 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()