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, " " 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