playfair

Playfair Script Formatter
git clone https://git.mulligrubs.me/playfair
Log | Files | Refs | README

commit d5e712cbb80abd7f7fa22385de46e7509af86454
parent 67032c458ccce2a84d8431b05e5dbba17a76a8bc
Author: St John Karp <git@stjohnkarp.net>
Date:   Tue, 30 Oct 2012 09:34:59 -0700

Added support for stage vs. screenplays

Added support for screenplays using Scriptfrenzy guidelines.
Common CSS is in a single file, while format-dependent CSS is in
separate files.

Also:
Added correct support for UTF-8
Removed the manual creation of temporary PDF files, which is more
secure for users worried about copyright.  Output is now directly
passed to the browser with Content-Type headers.

Diffstat:
Acommon.css | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjavascript.js | 4----
Mplayfair.pl | 49+++++++++++++++++++++++++++++--------------------
Mscript.php | 19+++++++++----------
Dscriptfrenzy.css | 105-------------------------------------------------------------------------------
Mscriptfrenzy_screen.css | 63++-------------------------------------------------------------
Ascriptfrenzy_stage.css | 39+++++++++++++++++++++++++++++++++++++++
7 files changed, 171 insertions(+), 200 deletions(-)

diff --git a/common.css b/common.css @@ -0,0 +1,92 @@ +@page { + margin: 1in 1in 1in 1.5in; + size: 8.5in 11in; /* Letter paper */ + @top-right { + font-family: monospace; + } +} + +@page meta { + @top-right { + content: normal; + } +} + +body { + font-family: monospace; + font-size: 12pt; +} + +div#titlepage,div#metapage { + page: meta; + page-break-after: always; +} + +div#play { + counter-reset: page 1; +} + +h1 { + page-break-after: avoid; + string-set: title content(); + text-align: center; + font-size: 1em; + font-style: normal; + font-weight: normal; +} + +p#author { + margin-top: 0.83em; + margin-bottom: 0.83em; + string-set: author content(); + text-align: center; +} + +hr.page { + page-break-after: always; + width: 0; +} + +p.level3 { + margin-top: 1em; + margin-bottom: 1em; + text-transform: uppercase; +} + +div.dialogue { + page-break-inside: avoid; +} + +p { + margin-top: 0; +} + +p.character { + margin-top: 1em; + margin-bottom: 0.04in; + text-transform: uppercase; +} + +p.dialogue { + margin-bottom: 0.04in; + margin-top: 0.04in; +} + +p.end { + margin-top: 4em; +} + +p.stageDirections { + margin-top: 1em; + margin-bottom: 1em; +} + +p.characterStageDirections { + margin-bottom: 0.04in; + margin-top: 0.04in; +} + +em { + font-style: normal; + text-decoration: underline; +} diff --git a/javascript.js b/javascript.js @@ -1,5 +1,3 @@ -<script type="text/javascript"> - function upperCase(text) { return text.toUpperCase(); } @@ -7,5 +5,3 @@ function upperCase(text) { function getAuthor() { return document.getElementById('authortag').getAttribute('last').toUpperCase(); } - -</script> diff --git a/playfair.pl b/playfair.pl @@ -2,8 +2,8 @@ % % Filename: playfair.pl % Author: St John Karp -% Date: 3 September 2011 -% Version: 1.0 +% Date: 30 October 2012 +% Version: 2.0 % % Purpose: % A program to format stage play scripts. @@ -23,13 +23,16 @@ play_to_html(Type, File):- halt. -script(Type, [element(html, [], [Head, Body, End])]) - --> head(Type, Head, Variables), double_break, body(Type, Body, Variables), double_break, end(End), single_break. +script(Type, [element(html, [], [Head, Body])]) + --> head(Type, Head, Variables), double_break, body(Type, Body, Variables). -body(stage, element(body, [], [TitlePage, MetaPage|Scenes]), [Title, Author, Personae, Time, Setting]) --> title_page(TitlePage, Title, Author), meta_page(MetaPage, Personae, Time, Setting), scene_repeater(stage, Scenes). +body(stage, element(body, [], [TitlePage, MetaPage, Play]), [Title, Author, Personae, Time, Setting]) --> title_page(TitlePage, Title, Author), meta_page(MetaPage, Personae, Time, Setting), play(stage, Play). -body(screen, element(body, [], [TitlePage|Scenes]), [Title, Author]) --> title_page(TitlePage, Title, Author), scene_repeater(screen, Scenes). +body(screen, element(body, [], [TitlePage, Play]), [Title, Author]) --> title_page(TitlePage, Title, Author), play(screen, Play). + + +play(Type, element(div, [id = play], Play)) --> scene_repeater(Type, Scenes), double_break, end(End), single_break, {append(Scenes, [End], Play)}. scene_repeater(Type, [Scene|Scenes]) --> scene(Type, Scene), double_break, scene_repeater(Type, Scenes). @@ -56,11 +59,9 @@ island_repeater([Island1|Island2]) --> island(Island1), double_break, island_rep island_repeater([Island]) --> island(Island). -island(element(div, [class = dialogue], [Character, CharacterStageDirections, Dialogue])) --> character(Character), single_break, character_stage_directions(CharacterStageDirections), single_break, dialogue(Dialogue). - island(element(div, [class = stageDirections], [StageDirections])) --> stage_directions(StageDirections). -island(element(div, [class = dialogue], [Character, Dialogue])) --> character(Character), single_break, dialogue(Dialogue). +island(element(div, [class = dialogue], [Character|Dialogue])) --> character(Character), single_break, dialogue_combo(Dialogue). scene_directions([element(p, [class = sceneDirections], [Text])]) --> text(['\n', <], Text). @@ -74,20 +75,25 @@ stage_directions(element(p, [class = stageDirections], [Text])) --> text(['\n'], character(element(p, [class = character], [Text])) --> text(['\n'], Text). -dialogue(element(p, [class = dialogue], [Unit])) --> dialogue_unit(Unit). +dialogue_combo([CharacterStageDirections, Dialogue|DialogueCombo]) --> character_stage_directions(CharacterStageDirections), single_break, dialogue(Dialogue), single_break, dialogue_combo(DialogueCombo). -dialogue(element(p, [class = dialogue], [Unit|Dialogue])) --> dialogue_unit(Unit), dialogue(element(p, [class = dialogue], Dialogue)). +dialogue_combo([CharacterStageDirections, Dialogue]) --> character_stage_directions(CharacterStageDirections), single_break, dialogue(Dialogue). + +dialogue_combo([Dialogue|DialogueCombo]) --> dialogue(Dialogue), single_break, dialogue_combo(DialogueCombo). -dialogue(element(p, [class = dialogue], [Unit, element(br, [], [])|Dialogue])) --> dialogue_unit(Unit), single_break, dialogue(element(p, [class = dialogue], Dialogue)). +dialogue_combo([Dialogue]) --> dialogue(Dialogue). -dialogue(element(p, [class = dialogue], [Unit, element(br, [], []), element(br, [], [])|Dialogue])) --> dialogue_unit(Unit), line_break(_), dialogue(element(p, [class = dialogue], Dialogue)). +dialogue(element(p, [class = dialogue], [Unit])) --> dialogue_unit(Unit). + +dialogue(element(p, [class = dialogue], [Unit|Dialogue])) --> dialogue_unit(Unit), dialogue(element(p, [class = dialogue], Dialogue)). -dialogue_unit(Text) --> text(['\n', <, >, '*', '(', ')'], Text). + +dialogue_unit(Text) --> text(['\n', <, >, '*'], Text). dialogue_unit(Emphatic) --> emphatic(Emphatic). -dialogue_unit(CDD) --> character_directions(CDD). +dialogue_unit(Break) --> line_break([Break]). character_stage_directions(element(p, [class = characterStageDirections], ['(', Text, ')'])) --> ['('], text(['\n', <, >, ')'], Text), [')']. @@ -103,9 +109,9 @@ emphatic(element(em, [], [Text])) --> ['*'], text(['\n', '*'], Text), ['*']. character_directions(element(span, [class = characterDirections], ['(', Text, ')'])) --> ['('], text(['\n', <, >, '(', ')'], Text), [')']. -head(stage, element(head, [], [Charset, TitleTag, AuthorTag, Styles]), [Title, Author, Personae, Time, Setting]) --> meta_charset(Charset), styles(stage, Styles), tag_title(TitleTag, Title), single_break, tag_author(AuthorTag, Author), single_break, tag_personae(Personae), single_break, tag_time(Time), single_break, tag_setting(Setting). +head(stage, element(head, [], [Charset, TitleTag, AuthorTag|Styles]), [Title, Author, Personae, Time, Setting]) --> meta_charset(Charset), styles(stage, Styles), tag_title(TitleTag, Title), single_break, tag_author(AuthorTag, Author), single_break, tag_personae(Personae), single_break, tag_time(Time), single_break, tag_setting(Setting). -head(screen, element(head, [], [Charset, TitleTag, AuthorTag, Styles]), [Title, Author]) --> meta_charset(Charset), styles(screen, Styles), tag_title(TitleTag, Title), single_break, tag_author(AuthorTag, Author). +head(screen, element(head, [], [Charset, TitleTag, AuthorTag|Styles]), [Title, Author]) --> meta_charset(Charset), styles(screen, Styles), tag_title(TitleTag, Title), single_break, tag_author(AuthorTag, Author). tag_title(element(title, [], [Text]), Text) --> ['@', t, i, t, l, e, ':', ' '], text(['\n'], Text). @@ -132,9 +138,12 @@ persona(element(p, [], [Text])) --> ['@', p, e, r, s, o, n, a, ':', ' '], text([ %date(element(meta, [name = date, content = ], [])) --> []. -styles(stage, element(link, [rel = 'stylesheet', type = 'text/css', href = 'scriptfrenzy.css'], [])) --> []. +styles(Type, [element(link, [rel = 'stylesheet', type = 'text/css', href = 'common.css'], []), Specific]) --> styles_specific(Type, Specific). + + +styles_specific(stage, element(link, [rel = 'stylesheet', type = 'text/css', href = 'scriptfrenzy_stage.css'], [])) --> []. -styles(screen, element(link, [rel = 'stylesheet', type = 'text/css', href = 'scriptfrenzy_screen.css'], [])) --> []. +styles_specific(screen, element(link, [rel = 'stylesheet', type = 'text/css', href = 'scriptfrenzy_screen.css'], [])) --> []. %meta_charset(element(meta, [charset = 'utf-8'], [])) --> []. @@ -163,7 +172,7 @@ slug(element(h2, [class = slug], ['I', 'N', 'T', '.', ' ', Text])) --> ['I', 'N' slug(element(h2, [class = slug], ['E', 'X', 'T', '.', ' ', Text])) --> ['E', 'X', 'T', '.', ' '], text(['\n'], Text). -end(element(p, [class = end], ['The End.'])) --> ['T', 'h', 'e', ' ', 'E', 'n', 'd', '.']. +end(element(p, [class = 'character end'], ['The End.'])) --> ['T', 'h', 'e', ' ', 'E', 'n', 'd', '.']. text(Forbidden, Text) --> no_funny_business(Forbidden, Text). diff --git a/script.php b/script.php @@ -1,16 +1,15 @@ <?php -if (($_FILES["file"]["type"] == "text/plain") && ($_FILES["file"]["size"] < 1000000)) { - if ($_FILES["file"]["error"] > 0) { - echo "Return Code: " . $_FILES["file"]["error"] . "<br />"; +if (($_FILES['script']['type'] == 'text/plain') && ($_FILES['script']['size'] < 1000000)) { + if ($_FILES['script']['error'] > 0) { + echo "Return Code: " . $_FILES['script']['error'] . "<br />"; } else { - echo("<p>Processing…</p>"); - $filename = time(); - system("swipl -s playfair.pl -g \"play_to_html('".$_FILES["file"]["tmp_name"]."')\" > $filename.html"); - system("prince --javascript --script javascript.js $filename.html -o $filename.pdf"); - header("Location: /$filename.pdf"); - sleep(60); - unlink("$filename.php"); + header("Content-Type: application/pdf"); + $script_name = pathinfo($_FILES['script']['name']); + $output_name = $script_name['filename'] . '.pdf'; + header("Content-Disposition:attachment; filename='$output_name'"); + passthru("LANG='en_US.UTF8' swipl -s playfair.pl -g \"play_to_html(".$_POST['type'].",'".$_FILES['script']['tmp_name']."')\" | prince - --javascript --script javascript.js"); + exit(); } } else { diff --git a/scriptfrenzy.css b/scriptfrenzy.css @@ -1,105 +0,0 @@ -@page { - margin: 1in; - size: 8.5in 11in; /* Letter paper */ -} - -@page { - @top-right { - content: prince-script(getAuthor) " / " prince-script(upperCase, string(title, first)) " / " counter(page); - } -} - -@page meta { - @top-right { - content: normal; - } -} - -body { - font-size: 12pt; -} - -div#titlepage,div#metapage { - counter-reset: page; - page: meta; -} - -h1 { - page-break-after: avoid; - string-set: title content(); - text-align: center; -} - -p#author { - font-size: 1.5em; - font-weight: bold; - margin-top: 0.83em; - margin-bottom: 0.83em; - string-set: author content(); - text-align: center; -} - -p.level3 { - font-size: 1.17em; - font-weight: bold; - margin-top: 1em; - margin-bottom: 1em; -} - -h3.actScene { - font-size: 1em; - font-weight: bold; - page-break-before: always; - text-align: center; -} - -hr.page { - page-break-after: always; - width: 0; -} - -div.dialogue { - page-break-inside: avoid; -} - -p { - margin-top: 0; -} - -p.character { - text-align: center; - margin-bottom: 0.04in; -} - -p.dialogue { - margin-bottom: 0.22in; -} - -p.end { - font-weight: bold; - text-align: center; -} - -p.sceneDirections { - font-style: italic; - margin-left: 2in; - margin-right: 1in; - margin-bottom: 0.08in; -} - -p.stageDirections { - font-style: italic; - margin-bottom: 0.22in; - margin-left: 1in; - margin-right: 1in; -} - -span.characterDirections { - font-style: italic; -} - -p.characterStageDirections { - font-style: italic; - text-align: center; - margin-bottom: 0.04in; -} diff --git a/scriptfrenzy_screen.css b/scriptfrenzy_screen.css @@ -1,79 +1,24 @@ @page { - margin: 1in 1in 1in 1.5in; - size: 8.5in 11in; /* Letter paper */ -} - -@page { @top-right { content: counter(page) "."; } } - -@page meta { - @top-right { - content: normal; - } -} - -body { - font-family: monospace; - font-size: 12pt; -} - -div#titlepage,div#metapage { - counter-reset: page; - page: meta; -} - -h1 { - page-break-after: avoid; - string-set: title content(); - text-align: center; - font-size: 1em; - font-style: normal; - font-weight: normal; -} - -p#author { - margin-top: 0.83em; - margin-bottom: 0.83em; - string-set: "by\n" author content(); - text-align: center; -} h2.slug { font-size: 1em; font-weight: normal; -} - -hr.page { - page-break-after: always; - width: 0; -} - -div.dialogue { - page-break-inside: avoid; -} - -p { - margin-top: 0; + margin-top: 1em; } p.character { margin-left: 2.2in; - margin-bottom: 0.04in; } p.dialogue { - margin-bottom: 0.22in; margin-left: 1in; margin-right: 1.5in; } -p.stageDirections { - margin-bottom: 0.22in; -} - span.characterDirections { } @@ -81,9 +26,5 @@ p.characterStageDirections { margin-left: 1.6in; margin-right: 1.9in; margin-bottom: 0.04in; -} - -em { - font-style: normal; - text-decoration: underline; + margin-top: 0.04in; } diff --git a/scriptfrenzy_stage.css b/scriptfrenzy_stage.css @@ -0,0 +1,39 @@ +@page { + @top-right { + content: prince-script(upperCase, string(title, first)) " / " counter(page); + } +} + +h3.actScene { + font-size: 1em; + page-break-before: always; + text-align: center; +} + +p.character { + margin-left: 2.5in; +} + +p.sceneDirections { + margin-left: 2in; + margin-right: 1in; + margin-top: 1em; +} + +p.sceneDirections:before,p.stageDirections:before { + content: "("; +} + +p.sceneDirections:after,p.stageDirections:after { + content: ")"; +} + +p.stageDirections { + margin-left: 0.5in; + margin-right: 1in; +} + +p.characterStageDirections { + margin-left: 1.5in; + margin-right: 1in; +}