File Coverage

blib/lib/App/makebeamerinfo.pm
Criterion Covered Total %
statement 105 145 72.4
branch 28 48 58.3
condition 13 22 59.0
subroutine 16 25 64.0
pod 0 14 0.0
total 162 254 63.7


line stmt bran cond sub pod time code
1             package App::makebeamerinfo;
2              
3 2     2   32355 use strict;
  2         4  
  2         83  
4 2     2   12 use warnings;
  2         4  
  2         167  
5              
6 2     2   23 use Cwd 'abs_path';
  2         4  
  2         135  
7 2     2   10 use File::Basename;
  2         3  
  2         202  
8 2     2   12 use File::Find;
  2         10  
  2         142  
9              
10 2     2   18002 use Text::Balanced qw/extract_bracketed extract_multiple/;
  2         84810  
  2         311  
11              
12             our $VERSION = "2.002";
13             $VERSION = eval $VERSION;
14              
15 2     2   1626 use App::makebeamerinfo::Transitions;
  2         7  
  2         4529  
16              
17             #==========================
18             # Builder methods
19              
20             sub new {
21 1     1 0 17 my $class = shift;
22 1 50       7 my $args = ref $_[0] ? shift() : { @_ };
23              
24 1         13 my $self = {
25             files => { #holder for file names
26             pdf => '',
27             nav => '',
28             },
29             pages => {}, #holder for page information from nav file
30             sections => {}, #holder for section information
31             transitions => {}, #holder for all available transition sets
32              
33             options => {
34             # set option to collapse AtBeginSection
35             # and AtBeginSubsection elements (default true)
36             collapse => 1,
37             transition_set => undef,
38             },
39             };
40              
41             # pull files from arguments if present
42 1 50       5 $self->{files}{pdf} = abs_path($args->{pdf}) if $args->{pdf};
43 1 50       4 $self->{files}{nav} = abs_path($args->{nav}) if $args->{nav};
44              
45 1         3 bless $self, $class;
46              
47 1         5 $self->_setup_standard_transition_sets;
48              
49 1   50     15 $self->transition_set( $args->{transition_set} || 'default' );
50              
51 1         4 $self->_hunt_for_files;
52              
53 1         4 return $self;
54            
55             }
56              
57             sub _setup_standard_transition_sets {
58 1     1   2 my $self = shift;
59 1         5 $self->add_transition_set('all', ':all');
60 1         3 $self->add_transition_set('default', ':default');
61 1         4 $self->add_transition_set('none', ':none');
62 1         19 $self->add_transition_set(
63             'turn',
64             increment => ["WipeRight"],
65             frame => ["PageTurn"],
66             );
67              
68             # the "most" transition set sorts the available transitions
69             # into the two uses as appropriate for a beamer presentation
70 1         18 $self->add_transition_set(
71             'most',
72             increment => [ qw/
73             WipeCenterIn WipeCenterOut
74             WipeUp WipeDown WipeLeft WipeRight
75             WipeDownRight WipeUpLeft
76             / ],
77             frame => [ qw/
78             Crossfade
79             PagePeel PageTurn
80             SlideDown SlideLeft SlideRight SlideUp
81             SpinOutIn SpiralOutIn
82             SqueezeDown SqueezeLeft SqueezeRight SqueezeUp
83             WipeBlobs
84             ZoomOutIn
85             / ],
86             );
87             }
88              
89             sub _hunt_for_files {
90 1     1   2 my $self = shift;
91 1         2 my $files = $self->{files};
92              
93 1 50 33     9 if (! $files->{pdf} and $files->{nav}) {
94 0         0 $files->{pdf} = $self->findFile( $files->{nav} );
95             }
96              
97 1 50 33     7 if (! $files->{nav} and $files->{pdf}) {
98 0         0 $files->{nav} = $self->findFile( $files->{pdf} );
99             }
100             }
101              
102             #=========================
103             # Transition set helpers
104              
105             sub add_transition_set {
106 5     5 0 8 my $self = shift;
107 5         7 my $name = $_[0];
108 5         27 return $self->{transitions}{$name} = App::makebeamerinfo::Transitions->new( @_ );
109             }
110              
111             sub transition_set {
112 5     5 0 2909 my $self = shift;
113 5 100       17 if ( my $name = shift ) {
114 4   100     26 my $trans = $self->{transitions}{$name} || die "Unknown transition set $name\n";
115 3         8 $self->{options}{transition_set} = $trans;
116             }
117 4         21 return $self->{options}{transition_set}->name
118             }
119              
120             #==========================
121             # common message methods
122              
123             # Sub that displays "about" information
124             # Possibly to be deprecated and replaced with a version sub and a usage sub
125             sub aboutMBI {
126 0     0 0 0 my $self = shift;
127 0         0 $self->userMessage(
128             "About MakeBeamerInfo",
129             "Version: $VERSION"
130             );
131             }
132              
133             # Sub (mostly for GUI) to display an exit message and quit before attempting to create an info file
134             sub exitProgramEarly {
135 0     0 0 0 my $self = shift;
136 0         0 $self->userMessage(
137             "Goodbye",
138             "No .info file has been created!"
139             );
140 0         0 exit(1);
141             }
142              
143             # Sub that after creation of an info file files, says goodbye and quits
144             sub exitProgramFinished {
145 0     0 0 0 my $self = shift;
146 0         0 $self->userMessage(
147             "Goodbye",
148             "Your .info file has been created."
149             );
150 0         0 exit();
151             }
152              
153             #============================
154             # Overloadable methods
155              
156             sub userMessage {
157 0     0 0 0 my $self = shift;
158 0         0 my ($title, $message) = @_;
159              
160             }
161              
162             sub run {
163 0     0 0 0 my $self = shift;
164 0         0 $self->createInfo;
165             }
166              
167             #============================
168             # Methods for finding and opening files
169              
170             # method that takes the full path of a specified file
171             # and returns the other file if possible
172             sub findFile {
173 0     0 0 0 my $self = shift;
174              
175             # burst the full file path into pieces
176 0 0       0 my $full_path = shift or return '';
177 0         0 my ($file, $dirs, $suffix) = fileparse( $full_path, '.pdf', '.nav' );
178              
179 0 0       0 $file .= ($suffix eq '.pdf') ? '.nav' : '.pdf';
180              
181 0         0 my $found = '';
182             my $wanted = sub {
183 0 0   0   0 return if $found;
184 0 0       0 if ($_ eq $file) {
185 0         0 $found = $File::Find::name;
186             }
187 0         0 };
188 0         0 find( $wanted , $dirs );
189              
190 0         0 return $found;
191             }
192              
193             sub openFile {
194 0     0 0 0 my $self = shift;
195 0         0 my ($filename, $type, $mode) = @_;
196              
197 0 0       0 unless ($filename) {
198 0         0 $self->userMessage(
199             "Error",
200             "Please specify a .$type file."
201             );
202 0         0 $self->exitProgramEarly();
203             }
204              
205 0         0 my $handle;
206 0 0       0 unless ( open($handle, $mode, $filename) ) {
207 0         0 $self->userMessage(
208             "Error",
209             "Could not open $filename: $!"
210             );
211 0         0 $self->exitProgramEarly();
212             }
213              
214 0         0 return $handle;
215             }
216              
217             #============================
218             # Subs that perform the "meat" of the work
219              
220             # super-sub that controls all the actions to generate info file
221             sub createInfo {
222 0     0 0 0 my $self = shift;
223 0         0 $self->readNav();
224 0         0 $self->writeInfo();
225 0         0 $self->exitProgramFinished();
226             }
227              
228             # sub to read the nav file. The information is fed into %pages and %sections.
229             # by reading twice we are able to use the collapse relate frames (declared after sections) and sections
230             sub readNav {
231 1     1 0 2362 my $self = shift;
232              
233             # if a handle is given as an arg use it. This is for testing.
234 1 50       5 my $nav = @_ ? shift :
235             $self->openFile($self->{files}{'nav'}, 'nav', '<');
236              
237 1         4 my $pages = $self->{pages};
238 1         2 my $sections = $self->{sections};
239 1         4 my $collapse = $self->{options}{collapse};
240              
241             # first read through the nav file for framepages
242 1         10 while (<$nav>) {
243 16 100       71 if( /\\beamer\@framepages\s*/gc ) {
244 3         9 my ($begin, $end) = tex_parser( $_, 2 );
245              
246 3         15 for ( my $i = $begin; $i < $end; $i++) {
247 2         15 $pages->{$i} = { page => $i, type => 'increment' };
248             }
249 3         25 $pages->{$end} = { page => $end, type => 'frame' };
250             }
251             }
252             # go back to the top of the .nav file
253 1         3 seek($nav,0,0);
254             # then read the file again to determine other information
255 1         7 while (<$nav>) {
256 16 100       35 if( /\\sectionentry\s*/gc ) {
257 1         3 my ($section, $title, $page) = tex_parser( $_, 3 );
258              
259 1         5 $sections->{$section}{'page'} = $page;
260 1         4 $sections->{$section}{'title'} = $title;
261 1         2 $pages->{$page}{'is_section'} = $section;
262             }
263 16 100       72 if( /\\beamer\@subsectionentry\s*/gc ) {
264 1         4 my (undef, $section, $subsection, $page, $title)
265             = tex_parser( $_, 5 );
266              
267 1         4 $pages->{$page}{'is_subsection'} = $subsection;
268 1         3 $pages->{$page}{'of_section'} = $section;
269 1         5 $sections->{$section}{$subsection}{'page'} = $page;
270 1         3 $sections->{$section}{$subsection}{'title'} = $title;
271 1 50 33     14 if ($collapse and $sections->{$section}{'page'} == ($page - 1)) {
272 0         0 $pages->{ $sections->{$section}{'page'} }{'to_collapse'} = 1;
273             }
274             }
275             }
276             }
277              
278             sub tex_parser {
279             # this function needs aliased arguments
280             # args: ( string with pos at start position, number of matches (optional) )
281              
282             # match {} blocks, See Text::Balanced for explaination
283             my @fields = extract_multiple(
284 14     14 0 1299 $_[0], [sub { extract_bracketed( $_[0], '{}' ) }], $_[1], 1
  5     5   42  
285             );
286              
287             # strip surrounding {}
288 5         538 return map { my $f = $_; $f =~ s/^\{//; $f =~ s/\}$//; $f } @fields;
  14         22  
  14         35  
  14         41  
  14         34  
289             }
290              
291             sub writeInfo {
292 3     3 0 35 my $self = shift;
293              
294             # if a handle is given as an arg use it. This is for testing.
295 3 50       12 my $info = @_ ? shift :
296             $self->openFile($self->{files}{'pdf'} . '.info', 'info', '>');
297              
298 3         5 my $pages = $self->{pages};
299 3         5 my $sections = $self->{sections};
300 3         7 my $trans = $self->{options}{transition_set};
301              
302 3         6 print $info "PageProps = {\n";
303 3         19 foreach my $page (sort { $a <=> $b } keys %$pages) {
  18         34  
304 15         40 print $info " " . $pages->{$page}{page} . ":\t{\n";
305 15 100 66     68 if ($pages->{$page}{'type'} eq 'increment' || $pages->{$page}{'to_collapse'}) {
306 6         13 print $info "\t \'overview\': False,\n";
307             }
308 15 100       46 if ($pages->{$page}{'is_section'}) {
    100          
309 3         12 print $info "\t \'title\': \"" . $sections->{ $pages->{$page}{'is_section'} }{'title'} . "\",\n";
310             } elsif ($page == 1) {
311 3         4 print $info "\t \'title\': \"Title\",\n";
312             }
313 15 100       33 if ($pages->{$page}{'is_subsection'}) {
314 3         17 print $info "\t \'title\': \"" . $sections->{ $pages->{$page}{'of_section'} }{'title'} . ": " . $sections->{ $pages->{$page}{'of_section'} }{ $pages->{$page}{'is_subsection'} }{'title'} . "\",\n";
315             }
316 15 100 66     81 if (
      100        
317             $pages->{$page}{'type'} eq 'frame'
318             && ! $pages->{$page}{'to_collapse'}
319             && ! $trans->default_frame
320             ) {
321 3         13 print $info "\t \'transition\': " . $trans->get_random_element . ",\n";
322             }
323 15         36 print $info "\t},\n";
324             }
325 3         8 print $info "}\n";
326 3 100       12 unless ( $trans->default_increment ) {
327 2         5 print $info "AvailableTransitions = [";
328 2         10 print $info join( ", ", $trans->get_selected('increment') );
329 2         15 print $info "]";
330             }
331             }
332              
333             1;
334              
335             __END__