commit 1395a2cc0cd5fd9e6683c729fd2e86975720574e
parent 5b71d8e39d8eec50b6bf531c5e8f45d49defb4a6
Author: St John Karp <contact@stjo.hn>
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:
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;
+}