1This 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
3Right 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
5I am not a published writer and not an authority on script formatting. I got what I know out of a book.
6
7Briefly, 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
9Thank 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
321