File Coverage

blib/lib/Text/Format/Screenplay.pm
Criterion Covered Total %
statement 135 147 91.8
branch 55 72 76.3
condition 8 12 66.6
subroutine 15 15 100.0
pod 2 2 100.0
total 215 248 86.6


line stmt bran cond sub pod time code
1             package Text::Format::Screenplay;
2              
3 1     1   24347 use 5.008009;
  1         4  
  1         47  
4 1     1   7 use strict;
  1         2  
  1         41  
5 1     1   5 use warnings;
  1         8  
  1         53  
6 1     1   1191 use File::Slurp qw(read_file);
  1         17851  
  1         83  
7 1     1   1342 use PDF::Create;
  1         57694  
  1         51  
8 1     1   2467 use Text::Wrap;
  1         3867  
  1         3270  
9              
10             our $VERSION = '0.01';
11              
12             my $DOUBLE_DIALOG_LINES = 0;
13             my $SHOW_SCENE_NUMBERS = 0;
14              
15             my $CHAR_WIDTH = 7;
16             my $CHAR_HEIGHT = 12;
17             my $PAGE_WIDTH = 612;
18             my $PAGE_HEIGHT = 792;
19              
20             # parse infile
21             my $TITLE = '';
22             my @AUTHOR = ();
23             my @CONTACT = ();
24             my $DRAFT = '';
25             my $DATE = '';
26              
27             sub new
28             {
29 1     1 1 12 my ($class, @args) = @_;
30 1         3 my $self = bless {}, $class;
31 1         5 return $self->_init();
32             }
33              
34             sub _init
35             {
36 1     1   3 my ($self, %opts) = @_;
37 1         2 return $self;
38             }
39              
40             sub pdf
41             {
42 1     1 1 18 my ($self, $filename, $outfile) = @_;
43              
44             # structure of $scenes: each element: [ 'type', 'content' ]
45 1         9 my $scenes = $self->_parse_infile( read_file($filename) );
46            
47 1         11 $self->_create_outfile( $scenes, $outfile );
48             }
49              
50             sub _parse_infile
51             {
52 1     1   250 my ($self, @lines) = @_;
53 1         2 my @sp;
54            
55 0         0 my $i; my $j;
56 1         6 for ($i=0; $i
57             {
58 17 50       56 next if $lines[$i] =~ /^[\s\t\n\r]*$/; # ignore blank lines
59 17 50       35 next if $lines[$i] =~ /^[\s\t\n\r]*\#/; # ignore comment lines
60            
61             # parse next paragraph
62 17         17 my $para = '';
63 17         39 for ($j=$i; $j
64 52         85 chomp($lines[$j]);
65 52 50       104 next if $lines[$j] =~ /^[\s\t\n\r]*\#/; # ignore comment lines
66 52 100       139 last if $lines[$j] =~ /^[\s\t\n\r]*$/; # ignore blank lines
67 36         108 $para .= $lines[$j];
68             }
69 17         23 $i = $j;
70            
71             # split into pre-colon and post-colon parts
72 17         72 my ($pre, $post) = $para =~ /^([^\:\~\>]*[\:\~\>]?)(.*)$/;
73 17 100       48 $post = '' unless $post;
74            
75 17 100       102 if ($pre eq 'title:') {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
76 1         9 ($TITLE) = uc($post) =~ /^[\s\t]*(.*)[\s\t\r\n]*$/;
77             } elsif ($pre eq 'author:') {
78 1         6 @AUTHOR = map { s/^[\s\t]*(.*)[\s\t\r\n]*$/$1/; $_ } split /;/, $post;
  1         7  
  1         7  
79             } elsif ($pre eq 'contact:') {
80 1         11 @CONTACT = map { s/^[\s\t]*(.*)[\s\t\r\n]*$/$1/; $_ } split /;/, $post;
  4         15  
  4         12  
81             } elsif ($pre eq 'draft:') {
82 1         4 $DRAFT = $post;
83             } elsif ($pre eq 'date:') {
84 1         12 $DATE = $post;
85             } elsif ($pre eq ':') {
86 3         13 push @sp, ['s', $post];
87             } elsif ($pre eq '>') {
88 1         5 push @sp, ['t', $post];
89             } elsif ($pre eq '~') {
90 1         6 push @sp, ['c', $post];
91             } elsif ($pre =~ /^.{1,20}\:$/) {
92 2         8 $pre =~ s/\:$//;
93 2         9 push @sp, [$pre, $post];
94             } else {
95 5         22 push @sp, ['d', $pre];
96             }
97             }
98            
99 1         9 return \@sp;
100             }
101              
102             sub _create_outfile
103             {
104 1     1   31 my ($self, $sp, $outfile) = @_;
105              
106 1         17 my $pdf = new PDF::Create
107             ('filename' => $outfile,
108             'Version' => 1.2,
109             'PageMode' => 'UseOutlines',
110             'Author' => join(', ', @AUTHOR),
111             'Title' => $TITLE,
112             );
113 1         382 my $root = $pdf->new_page('Media-Box' => [0, $PAGE_WIDTH, 0, $PAGE_HEIGHT]);
114 1         73 my $font = $pdf->font
115             ('Subtype' => 'Type1',
116             'Encoding' => 'WinAnsiEncoding',
117             'BaseFont' => 'CourierNew');
118              
119 1         42 my $mid = sprintf "%.0f", ($PAGE_WIDTH / $CHAR_WIDTH / 2);
120              
121             #
122             # title page
123             #
124 1         4 my $page = $root->new_page; # inherit from root
125              
126 1         48 my $line = 25;
127 1         5 $line = _put($page, $font, $TITLE, $line, _mid($mid,$TITLE));
128 1         2 $line += 4;
129 1         3 $line = _put($page, $font, "written by", $line, _mid($mid,"written by"));
130 1         1 $line++;
131 1         2 foreach (@AUTHOR) { $line = _put($page, $font, $_, $line, _mid($mid,$_)) }
  1         10  
132 1         5 _put($page, $font, join("\n",@CONTACT), 61-scalar(@CONTACT), 15);
133 1         4 _put($page, $font, $DRAFT, 56, 62);
134 1         3 _put($page, $font, $DATE, 60, 62);
135              
136             #
137             # script pages
138             #
139 1         2 my $pnum = 1; # page counter
140 1         1 my $snum = 1; # scene counter
141            
142 1         4 $page = $root->new_page;
143 1         55 _put($page, $font, sprintf("%10s", "$pnum."), 4, 62);
144 1         2 $line = 6;
145 1         3 for (my $e=0; $e<@{$sp}; $e++)
  13         28  
146             {
147 12         19 my $type = $sp->[$e]->[0];
148 12         22 my $text = $sp->[$e]->[1];
149            
150 12 100       44 if ($type !~ /^[stcd]$/) { $text =~ s/\(/\n\(/g; $text =~ s/\)/)\n/g }
  2         5  
  2         5  
151              
152             # check if current page is full
153 12         12 my $till = 0; # till what line the next element needs space
154 12 100 100     48 if ($type eq 's') {
    100          
    100          
155             # own + 1 line space + space for next part
156 3         6 $till = $line + _sizeof($text,1000);
157 3         4 $till += 10;
158             } elsif ($type eq 't' || $type eq 'c') {
159             # one line (+ 2 empty lines of not last line)
160 2         3 $till = $line + 1;
161 2 50       4 $till += 2 unless $till == 60;
162             } elsif ($type eq 'd') {
163             # own size (+ 1 empty line if not on last line)
164 5         6 $till = $line + _sizeof($text,54);
165 5 50       12 $till++ unless $till == 60;
166             } else {
167             # name line + dialog lines
168 2         6 $till = $line + 1 + _sizeof($text,32);
169 2 50       6 $till += _sizeof($text,32) if $DOUBLE_DIALOG_LINES;
170             }
171            
172             # create new page
173 12 50       21 if ($till > 60) {
174 0         0 $pnum++;
175 0         0 $page = $root->new_page;
176 0         0 _put($page, $font, sprintf("%10s", "$pnum."), 4, 62);
177 0         0 $line = 6;
178 0         0 next;
179             }
180              
181             # add current element + space to page
182 12 100       37 if ($type eq 's') {
    100          
    100          
    100          
183 3 50       5 if ($SHOW_SCENE_NUMBERS) {
184 0         0 _put($page, $font, sprintf("%5s",$snum), $line, 13);
185 0         0 _put($page, $font, $snum, $line, 74);
186 0         0 $snum++;
187             }
188 3         5 $line = _put($page, $font, $text, $line, 19);
189 3         4 $line++;
190             } elsif ($type eq 't') {
191 1         3 $line = _put($page, $font, $text, $line, 62);
192 1 50       6 $line += 2 unless $line == 60;
193             } elsif ($type eq 'c') {
194 1         5 $line = _put($page, $font, $text, $line, _mid($mid,$text));
195 1 50       4 $line += 2 unless $line == 60;
196             } elsif ($type eq 'd') {
197 5         7 $line = _put($page, $font, _wrapit($text,54), $line, 19);
198 5 100 100     7 if (($e+1 < scalar(@{$sp})) &&
  5   33     49  
199             ($sp->[$e+1]->[0] eq 't' || $sp->[$e+1]->[0] eq 'c' ||
200             $sp->[$e+1]->[0] eq 's')) {
201 3 50       10 $line += 2 unless $line == 60;
202             } else {
203 2 50       7 $line++ unless $line == 60;
204             }
205             } else {
206 2         4 $line = _put($page, $font, $type, $line, 43);
207 2         4 $text = _wrapit($text,32); $text =~ s/\(/ \(/g;
  2         5  
208 2 50       6 if ($DOUBLE_DIALOG_LINES) {
209 0         0 $text =~ s/\n/\n\n/g; $text = "\n$text" }
  0         0  
210 2         3 $line = _put($page, $font, $text, $line, 29);
211 2 50 33     3 if (($e+1 < scalar(@{$sp})) && ($sp->[$e+1]->[0] !~ /^stcd$/)) {
  2         15  
212 2 50       6 $line++ unless $line == 60;
213             } else {
214 0 0       0 $line += 2 unless $line == 60;
215             }
216             }
217             }
218              
219 1         5 $pdf->close;
220             }
221              
222             sub _sizeof
223             {
224 10     10   12 my ($text, $width) = @_;
225 10         15 $text = _wrapit($text, $width);
226 10         28 my @lines = split /\n/, $text;
227 10         20 return scalar(@lines);
228             }
229              
230             sub _wrapit
231             {
232 17     17   19 my ($text, $width) = @_;
233 17         15 $Text::Wrap::columns = $width;
234 17         34 $text = Text::Wrap::wrap("", "", $text);
235 17         4479 $text =~ s/\s*\n\s*/\n/g;
236 17         54 $text =~ s/^\s*//g;
237 17         33 return $text;
238             }
239              
240             sub _mid
241             {
242 4     4   7 my ($mid, $str) = @_;
243 4         38 return sprintf("%.0f", $mid - (length($str) / 2));
244             }
245              
246             # places a text block onto a page, starting at a certain position
247             sub _put
248             {
249 21     21   38 my ($page, $font, $text, $line, $column) = @_;
250 21         56 foreach (split /\n/, $text) {
251 41         124 $page->stringl($font, 10,
252             ($column * $CHAR_WIDTH),
253             ($PAGE_HEIGHT - ($line * $CHAR_HEIGHT)), $_);
254 41         2620 $line++;
255             }
256 21         40 return $line;
257             }
258              
259             1;
260             __END__