ikiwiki (3.20130711) unstable; urgency=low
[ikiwiki.git] / doc / plugins / contrib / screenplay.pm.mdwn
1 This plugin works for me. It follows the standard for a movie screenplay pretty closely, I am not aware of any errors in format. Please let me know if you find any.
2
3 Right now all it does is display your pages properly in a web browser. What I would like to add is the ability to output a file that could easily be printed once the screenplay is finished. We keep all the scenes we work on in one folder and eventually we will want to print a script out of that folder. It would be great if an up to date PDF or TXT script could be put in the folder when a scene is saved. I will do it, it just isn't a priority yet. 
4
5 I am not a published writer and not an authority on script formatting. I got what I know out of a book.
6
7 Briefly, you type a command on a line, like ".d", then on the next line (for the dialog command) you type a person's name. Then you hit return again and write the words he is supposed to speak out all on one line. When you save your document this simple text will become a properly formatted script.
8
9 Thank you Joey for having me here.
10
11 ###Headings: 
12         Most headings should begin with a transition. The list of valid commands is:
13         .fi    =>    FADE IN: a gradual transition from a solid color to an image 
14         .fo    =>    FADE OUT.
15         .ftb    =>    FADE TO BLACK.
16         .ftw    =>    FADE TO WHITE.
17         .ct     =>    CUT TO: indicates an instantaneous shift from one shot to the next
18         .shot    =>    lack of an explicit transition assumes a cut
19         .hct    =>    HARD CUT TO: describes a jarring transition
20         .qct    =>    QUICK CUT TO: describes a cut sooner than expected
21         .tct    =>    TIME CUT TO: emphasizes time passing
22         .mct    =>    MATCH CUT TO: image in first shot visually or thematically matches image in second
23         .dt    =>    DISSOLVE TO: gradual transition from image to another implies passage of time.
24         .rdt    =>    RIPPLE DISSOLVE TO: indicates transition into daydream or imagination
25         .wt    =>    WIPE TO: new image slides over top of last one
26         
27         Example transition:
28         
29         .fi (or any transition command) <= Writes a transition line, except .shot which omits it.
30         type shot heading here   <= this line will be capitalized
31         First direction.   <= these lines are not capitalized.
32         Second direction.
33         Third direction, etc...
34         
35         Direction without a shot heading:
36         .dir
37         First direction.
38         Second direction.
39         Third direction, etc...
40         
41         Some items aren't implemented in dialogue yet:
42         1) you must watch that you don't leave a " -- " dangling on a line by itself, 
43              instead, carry the last word onto the line with a dash
44         2) observe lyrical line endings in dialogue by indenting wrapped lines by two spaces
45         3) you must watch that the four line limit for parenthetical direction is not exceeded
46         
47         Example dialogue:
48         
49         .d 
50         char name   <= this line will be capitalized
51         this is what he's saying                  <= Dialogue
52         raises hand to wave                       <= Parenthetical direction
53         this is more of what he's saying          <= Dialogue
54         this is going to be in parenthesis        <= Parenthetical direction
55         this is more of what he's saying, etc...  <= Dialogue
56         
57         .note 
58         Allows you to add a temporary note to a script without getting an error. 
59         All notes need to be removed eventually because they are a format violation.
60         
61         
62         
63         ###name this file screenplay.pm and pop it in your Plugin folder. Then you need to add the plugin to your Ikiwiki setup file.
64         
65         #!/usr/bin/perl
66         # Screenplay markup language
67         package IkiWiki::Plugin::screenplay;
68         
69         use warnings;
70         use strict;
71         use IkiWiki 3.00;
72         use Text::Format;
73         use Log::Log4perl qw(:easy);
74         Log::Log4perl->easy_init($INFO);
75         #Log::Log4perl->easy_init($ERROR);
76         
77         sub import {
78                 hook(type => "getsetup", id => "screenplay", call => \&getsetup);
79                 hook(type => "htmlize", id => "screenplay", call => \&htmlize, longname => "Screenplay");
80         }
81         
82         sub getsetup () {
83                 return
84                         plugin => {
85                                 safe => 1,
86                                 rebuild => 1, # format plugin
87                                 section => "format",
88                         },
89         }
90         
91         sub htmlize (@) {
92                 #set up variables and fill with defaults
93                 my %params=@_;
94                 my $content = $params{content};
95                 my @lines = split(/\r\n|\r|\n/, $content);
96                 my @chunk;
97                 my @formatted;
98                 my $current_line = shift(@lines);
99                 my $current_command = "";
100                 my $current_chunk = "";
101         
102             while (scalar(@lines) > 0) {
103                 until ( &dot_command($current_line) || scalar(@lines) == 0 ) {
104                     #skip spaces; mark bad lines
105                     unless ( &blank_line($current_line) ) {
106                         push(@formatted, "<br />");
107                         push(@formatted, &no_command($current_line));
108                     }
109                     $current_line = shift(@lines);
110                 }
111         
112                 #Exit while loop if we're out of lines
113                 last if (scalar(@lines) == 0);
114         
115                 #set command for chunk
116                 $current_command = $current_line;
117                 $current_line = shift(@lines);
118         
119                 #get chunk, i.e. all text up to next blank line or a dot command.
120                 until (substr($current_line,0,1) eq '.' || $current_line =~ m// || $current_line =~ m/^\s*$/) {
121                     push(@chunk,$current_line);
122                     $current_line = shift(@lines);
123                     last unless defined $current_line;
124                 } 
125         
126                 #Start with a blank line unless unneeded.
127                 if (scalar(@formatted) > 0 ) {
128                         push(@formatted, "<br />");
129                 }
130         
131                 #remaining lines are not commands.
132                 if (scalar(@chunk)) {
133                         $current_chunk = shift(@chunk);
134                         if ($current_command eq ".shot") {
135                                 push(@formatted, &indent(&chunk(uc($current_chunk),57),17));
136                                 while (scalar(@chunk)) {
137                                         $current_chunk = shift(@chunk);
138                                         push(@formatted, "<br />");
139                                         push(@formatted, &indent(&chunk($current_chunk,57),17));
140                                 }
141         
142                         } elsif ($current_command eq ".note") { 
143                                 push(@formatted, "NOTE:<br />");
144                                 push(@formatted, &chunk($current_chunk,75));
145                                 while (scalar(@chunk)) {
146                                         $current_chunk = shift(@chunk);
147                                         push(@formatted, "<br />");
148                                         push(@formatted, &chunk($current_chunk,75));
149                                 }
150         
151                         } elsif ($current_command eq ".dir") {
152                                 push(@formatted, &indent(&chunk($current_chunk,57),17));
153                                 while (scalar(@chunk)) {
154                                         $current_chunk = shift(@chunk);
155                                         push(@formatted, "<br />");
156                                         push(@formatted, &indent(&chunk($current_chunk,57),17));
157                                 }               
158         
159                         } elsif ($current_command eq ".d") {
160                                 push(@formatted, &indent(&chunk(uc($current_chunk),32),41));
161                                 $current_chunk = shift(@chunk);
162                                 push(@formatted, &indent(&chunk($current_chunk,34),27));
163                                 while (scalar(@chunk) / 2 >= 1 ) {
164                                         $current_chunk = shift(@chunk);
165                                         push(@formatted, &indent(&chunk(&pd($current_chunk),19),34));
166                                         $current_chunk = shift(@chunk);
167                                         push(@formatted, &indent(&chunk($current_chunk,34),27));
168                                 }
169         
170                         } elsif ($current_command eq ".pd") {
171                                 push(@formatted, &indent(&chunk(uc($current_chunk),32),41));
172                                 $current_chunk = shift(@chunk);
173                                 push(@formatted, &indent(&chunk(&pd($current_chunk),19),34));
174                                 $current_chunk = shift(@chunk);
175                                 push(@formatted, &indent(&chunk($current_chunk,34),27));
176                                 while (scalar(@chunk) / 2 >= 1 ) {
177                                         $current_chunk = shift(@chunk);
178                                         push(@formatted, &indent(&chunk(&pd($current_chunk),19),34));
179                                         $current_chunk = shift(@chunk);
180                                         push(@formatted, &indent(&chunk($current_chunk,34),27));
181                                 }
182         
183                         } elsif ($current_command =~ m/^\.(fi|fo|ct|hct|qct|tct|mct|dt|rdt)$/) {
184                                 if ($current_command eq ".fi") {
185                                         push(@formatted, &indent(&chunk(uc("FADE IN:"),20),17));
186                                 } elsif ($current_command eq ".fo") {
187                                         push(@formatted, &indent(&chunk(uc("FADE OUT:"),20),60));
188                                 } elsif ($current_command eq ".ct") {
189                                         push(@formatted, &indent(&chunk(uc("CUT TO:"),20),60));
190                                 } elsif ($current_command eq ".hct") {
191                                         push(@formatted, &indent(&chunk(uc("HARD CUT TO:"),20),60));
192                                 } elsif ($current_command eq ".qct") {
193                                         push(@formatted, &indent(&chunk(uc("QUICK CUT TO:"),20),60));
194                                 } elsif ($current_command eq ".tct") {
195                                         push(@formatted, &indent(&chunk(uc("TIME CUT TO:"),20),60));
196                                 } elsif ($current_command eq ".mct") {
197                                         push(@formatted, &indent(&chunk(uc("MATCH CUT TO:"),20),60));
198                                 } elsif ($current_command eq ".dt") {
199                                         push(@formatted, &indent(&chunk(uc("DISSOLVE TO:"),20),60));
200                                 } elsif ($current_command eq ".rdt") {
201                                         push(@formatted, &indent(&chunk(uc("RIPPLE DISSOLVE TO:"),20),60));
202                                 } elsif ($current_command eq ".wt") {
203                                         push(@formatted, &indent(&chunk(uc("WIPE TO:"),20),60));
204                                 }
205                                 push(@formatted, &indent(&chunk(uc($current_chunk),57),17));
206                                 while (scalar(@chunk)) {
207                                         $current_chunk = shift(@chunk);
208                                         push(@formatted, "<br />");
209                                         push(@formatted, &indent(&chunk($current_chunk,57),17));
210                                 }
211                                                 
212                         }
213                         #mark the rest of the chunk as 'no command'
214                         if (scalar(@chunk)) {
215                                 $current_chunk = shift(@chunk);
216                                 push(@formatted, &no_command($current_chunk));
217                         }
218         
219                 }
220             }
221             my @content;
222             my $i = 0;
223             $current_line = "";
224             while (scalar(@formatted)) {
225                 $i++;
226                 $current_line = shift(@formatted);      
227                 if ( $i % 60 == 0 ) {
228                     push(@content, &indent($i/60  . ".<br />",72) );
229                 }
230                 push(@content, $current_line);
231             }
232             $content = join("\r\n",@content);
233             return $content;
234         }
235         
236         sub blank_line {
237                 my $line = shift(@_);
238                 my $ret = 0;
239         
240                 if ($line =~ m// || $line =~ m/^\s*$/) {
241                     $ret = 1;
242                 } else {
243                     $ret = 0;
244                 }
245         
246                 return $ret;
247         }
248         
249         sub chunk () {
250                 my $unchunked = shift(@_);
251                 my $columns = shift(@_);
252                 my $text = new Text::Format;
253                 $text->rightFill(1);
254                 $text->columns($columns);
255                 $text->firstIndent(0);
256                 $text->tabstop(0);
257                 $text->extraSpace(1);
258                 my @chunked = split /\n/, $text->format($unchunked);
259                 my @formatted;
260                 foreach (@chunked) {
261                         push(@formatted, $_ . "<br />");
262                 }
263                 return @formatted;
264         }
265         
266         sub dot_command {
267                 my $line = shift(@_);
268                 my $ret = 0;
269         
270                 if ($line =~ m/^\.(ct|dir|dt|d|fi|fo|hct|mct|note|pd|qct|rdt|shot|tct)$/) {
271                     $ret = 1;
272                 } else {
273                     $ret = 0;
274                 }
275         
276                 return $ret;
277         }
278         
279         sub indent () {
280                 my @unindented = @_;
281                 my $spaces = pop @unindented;
282                 my @indented;
283                 foreach (@unindented) {
284                         push(@indented, "&nbsp;" x $spaces . $_);
285                 }
286                 return @indented;
287         }
288         
289         sub no_command () {
290                 my $line = shift(@_);
291                   my $text = new Text::Format;
292                 $text->rightFill(1);
293                 $text->columns(68);
294                 $text->firstIndent(0);
295                 $text->tabstop(0);
296                 $text->extraSpace(1);
297                 my @chunked = split /\n/, $text->format($line);
298                 my @formatted;
299                 push(@formatted, ("NO COMMAND: "));
300                 foreach (@chunked) {
301                         push(@formatted, ( $_ . "<br />" ));
302                 }
303                 return @formatted;
304         }
305         
306         sub pd () {
307                 my @chunk = @_;
308                 # add '(' to top item
309                 my $line = "(" . shift(@chunk);
310                 unshift(@chunk, $line);
311         
312                 # add ')' to bottom item
313                 $line = pop(@chunk) . ")";
314                 push(@chunk, $line);
315         
316                 return @chunk;
317         }
318         
319         1
320