File Coverage

lib/Win32/PowerPoint.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Win32::PowerPoint;
2            
3 1     1   1037 use strict;
  1         2  
  1         32  
4 1     1   5 use warnings;
  1         1  
  1         26  
5 1     1   4 use Carp;
  1         2  
  1         52  
6 1     1   5 use base qw( Class::Accessor::Fast );
  1         1  
  1         797  
7            
8             our $VERSION = '0.09';
9            
10 1     1   3987 use File::Spec;
  1         3  
  1         20  
11 1     1   5 use File::Basename;
  1         11  
  1         76  
12 1     1   429 use Win32::OLE;
  0            
  0            
13             use Win32::PowerPoint::Constants;
14             use Win32::PowerPoint::Utils qw(
15             RGB
16             canonical_alignment
17             canonical_pattern
18             canonical_datetime
19             convert_cygwin_path
20             _defined_or
21             );
22            
23             __PACKAGE__->mk_ro_accessors( qw( c application presentation slide ) );
24            
25             sub new {
26             my $class = shift;
27             my $self = bless {
28             c => Win32::PowerPoint::Constants->new,
29             was_invoked => 0,
30             application => undef,
31             presentation => undef,
32             slide => undef,
33             }, $class;
34            
35             $self->connect_or_invoke;
36            
37             return $self;
38             }
39            
40             ##### application #####
41            
42             sub connect_or_invoke {
43             my $self = shift;
44            
45             $self->{application} = Win32::OLE->GetActiveObject('PowerPoint.Application');
46            
47             unless (defined $self->{application}) {
48             $self->{application} = Win32::OLE->new('PowerPoint.Application')
49             or die Win32::OLE->LastError;
50             $self->{was_invoked} = 1;
51             }
52             }
53            
54             sub quit {
55             my $self = shift;
56            
57             return unless $self->application;
58            
59             $self->application->Quit;
60             $self->{application} = undef;
61             }
62            
63             ##### presentation #####
64            
65             sub new_presentation {
66             my $self = shift;
67            
68             return unless $self->{application};
69            
70             my %options = ( @_ == 1 and ref $_[0] eq 'HASH' ) ? %{ $_[0] } : @_;
71            
72             $self->{slide} = undef;
73            
74             $self->{presentation} = $self->application->Presentations->Add
75             or die Win32::OLE->LastError;
76            
77             $self->_apply_background(
78             $self->presentation->SlideMaster->Background->Fill,
79             %options
80             );
81             }
82            
83             sub _apply_background {
84             my ($self, $target, %options) = @_;
85            
86             my $forecolor = _defined_or(
87             $options{background_forecolor},
88             $options{masterbkgforecolor}
89             );
90             if ( defined $forecolor ) {
91             $target->ForeColor->{RGB} = RGB($forecolor);
92             $self->slide->{FollowMasterBackground} = $self->c->msoFalse if $options{slide};
93             }
94            
95             my $backcolor = _defined_or(
96             $options{background_backcolor},
97             $options{masterbkgbackcolor}
98             );
99             if ( defined $backcolor ) {
100             $target->BackColor->{RGB} = RGB($backcolor);
101             $self->slide->{FollowMasterBackground} = $self->c->msoFalse if $options{slide};
102             }
103            
104             if ( defined $options{pattern} ) {
105             if ( $options{pattern} =~ /\D/ ) {
106             my $method = canonical_pattern($options{pattern});
107             $options{pattern} = $self->c->$method;
108             }
109             $target->Patterned( $options{pattern} );
110             }
111             }
112            
113             sub save_presentation {
114             my ($self, $file) = @_;
115            
116             return unless $self->presentation;
117             return unless defined $file;
118            
119             my $absfile = File::Spec->rel2abs($file);
120             my $directory = dirname( $file );
121             unless (-d $directory) {
122             require File::Path;
123             File::Path::mkpath($directory);
124             }
125            
126             $self->presentation->SaveAs( convert_cygwin_path( $absfile ) );
127             }
128            
129             sub close_presentation {
130             my $self = shift;
131            
132             return unless $self->presentation;
133            
134             $self->presentation->Close;
135             $self->{presentation} = undef;
136             }
137            
138             sub set_master_footer {
139             my $self = shift;
140            
141             return unless $self->presentation;
142             my $master_footers = $self->presentation->SlideMaster;
143             $self->_set_footer($master_footers, @_);
144             }
145            
146             sub _set_footer {
147             my ($self, $slide, @args) = @_;
148            
149             my $target = $slide->HeadersFooters;
150            
151             my %options = ( @args == 1 and ref $args[0] eq 'HASH' ) ? %{ $args[0] } : @args;
152            
153             if ( defined $options{visible} ) {
154             $target->Footer->{Visible} = $options{visible} ? $self->c->msoTrue : $self->c->msoFalse;
155             }
156            
157             if ( defined $options{text} ) {
158             $target->Footer->{Text} = $options{text};
159             }
160            
161             if ( defined $options{slide_number} ) {
162             $target->SlideNumber->{Visible} = $options{slide_number} ? $self->c->msoTrue : $self->c->msoFalse;
163             }
164            
165             if ( defined $options{datetime} ) {
166             $target->DateAndTime->{Visible} = $options{datetime} ? $self->c->msoTrue : $self->c->msoFalse;
167             }
168            
169             if ( defined $options{datetime_format} ) {
170             if ( !$options{datetime_format} ) {
171             $target->DateAndTime->{UseFormat} = $self->c->msoFalse;
172             }
173             else {
174             if ( $options{datetime_format} =~ /\D/ ) {
175             my $format = canonical_datetime($options{datetime_format});
176             $options{datetime_format} = $self->c->$format;
177             }
178             $target->DateAndTime->{UseFormat} = $self->c->msoTrue;
179             $target->DateAndTime->{Format} = $options{datetime_format};
180             }
181             }
182             }
183            
184             ##### slide #####
185            
186             sub new_slide {
187             my $self = shift;
188            
189             my %options = ( @_ == 1 and ref $_[0] eq 'HASH' ) ? %{ $_[0] } : @_;
190            
191             $self->{slide} = $self->presentation->Slides->Add(
192             $self->presentation->Slides->Count + 1,
193             $self->c->LayoutBlank
194             ) or die Win32::OLE->LastError;
195            
196             $self->_apply_background(
197             $self->slide->Background->Fill,
198             %options,
199             slide => 1,
200             );
201             }
202            
203             sub set_footer {
204             my $self = shift;
205            
206             return unless $self->slide;
207             $self->_set_footer($self->slide, @_);
208             }
209            
210             sub add_text {
211             my ($self, $text, $options) = @_;
212            
213             return unless $self->slide;
214             return unless defined $text;
215            
216             $options = {} unless ref $options eq 'HASH';
217            
218             $text =~ s/\n/\r/gs;
219            
220             my $num_of_boxes = $self->slide->Shapes->Count;
221             my $last = $num_of_boxes ? $self->slide->Shapes($num_of_boxes) : undef;
222             my ($left, $top, $width, $height);
223             if ($last) {
224             $left = _defined_or($options->{left}, $last->Left);
225             $top = _defined_or($options->{top}, $last->Top + $last->Height + 20);
226             $width = _defined_or($options->{width}, $last->Width);
227             $height = _defined_or($options->{height}, $last->Height);
228             }
229             else {
230             $left = _defined_or($options->{left}, 30);
231             $top = _defined_or($options->{top}, 30);
232             $width = _defined_or($options->{width}, 600);
233             $height = _defined_or($options->{height}, 200);
234             }
235            
236             my $new_textbox = $self->slide->Shapes->AddTextbox(
237             $self->c->TextOrientationHorizontal,
238             $left, $top, $width, $height
239             );
240            
241             my $frame = $new_textbox->TextFrame;
242             my $range = $frame->TextRange;
243            
244             $frame->{WordWrap} = $self->c->True;
245             $range->ParagraphFormat->{FarEastLineBreakControl} = $self->c->True;
246             $range->{Text} = $text;
247            
248             $self->decorate_range( $range, $options );
249            
250             $frame->{AutoSize} = $self->c->AutoSizeNone;
251             $frame->{AutoSize} = $self->c->AutoSizeShapeToFitText;
252            
253             return $new_textbox;
254             }
255            
256             sub add_picture {
257             my ($self, $file, $options) = @_;
258            
259             return unless $self->slide;
260             return unless defined $file and -f $file;
261            
262             $options = {} unless ref $options eq 'HASH';
263            
264             my $num_of_boxes = $self->slide->Shapes->Count;
265             my $last = $num_of_boxes ? $self->slide->Shapes($num_of_boxes) : undef;
266             my ($left, $top);
267             if ($last) {
268             $left = _defined_or($options->{left}, $last->Left);
269             $top = _defined_or($options->{top}, $last->Top + $last->Height + 20);
270             }
271             else {
272             $left = _defined_or($options->{left}, 30);
273             $top = _defined_or($options->{top}, 30);
274             }
275            
276             my $new_picture = $self->slide->Shapes->AddPicture(
277             convert_cygwin_path( $file ),
278             ( $options->{link}
279             ? ( $self->c->msoTrue, $self->c->msoFalse )
280             : ( $self->c->msoFalse, $self->c->msoTrue )
281             ),
282             $left, $top, $options->{width}, $options->{height}
283             );
284            
285             return $new_picture;
286             }
287            
288             sub insert_before {
289             my ($self, $text, $options) = @_;
290            
291             return unless $self->slide;
292             return unless defined $text;
293            
294             $options = {} unless ref $options eq 'HASH';
295            
296             $text =~ s/\n/\r/gs;
297            
298             my $num_of_boxes = $self->slide->Shapes->Count;
299             my $last = $num_of_boxes ? $self->slide->Shapes($num_of_boxes) : undef;
300             my $range = $self->slide->Shapes($num_of_boxes)->TextFrame->TextRange;
301            
302             my $selection = $range->InsertBefore($text);
303            
304             $self->decorate_range( $selection, $options );
305            
306             return $selection;
307             }
308            
309             sub insert_after {
310             my ($self, $text, $options) = @_;
311            
312             return unless $self->slide;
313             return unless defined $text;
314            
315             $options = {} unless ref $options eq 'HASH';
316            
317             $text =~ s/\n/\r/gs;
318            
319             my $num_of_boxes = $self->slide->Shapes->Count;
320             my $last = $num_of_boxes ? $self->slide->Shapes($num_of_boxes) : undef;
321             my $range = $self->{slide}->Shapes($num_of_boxes)->TextFrame->TextRange;
322            
323             my $selection = $range->InsertAfter($text);
324            
325             $self->decorate_range( $selection, $options );
326            
327             return $selection;
328             }
329            
330             sub decorate_range {
331             my ($self, $range, $options) = @_;
332            
333             return unless defined $range;
334            
335             $options = {} unless ref $options eq 'HASH';
336            
337             my ($true, $false) = ($self->c->True, $self->c->False);
338            
339             $range->Font->{Bold} = $options->{bold} ? $true : $false;
340             $range->Font->{Italic} = $options->{italic} ? $true : $false;
341             $range->Font->{Underline} = $options->{underline} ? $true : $false;
342             $range->Font->{Shadow} = $options->{shadow} ? $true : $false;
343             $range->Font->{Subscript} = $options->{subscript} ? $true : $false;
344             $range->Font->{Superscript} = $options->{superscript} ? $true : $false;
345             $range->Font->{Size} = $options->{size} if $options->{size};
346             $range->Font->{Name} = $options->{name} if $options->{name};
347             $range->Font->{Name} = $options->{font} if $options->{font};
348             $range->Font->Color->{RGB} = RGB($options->{color}) if $options->{color};
349            
350             my $align = $options->{alignment} || $options->{align} || 'left';
351             if ( $align =~ /\D/ ) {
352             my $method = canonical_alignment( $align );
353             $align = $self->c->$method;
354             }
355             $range->ParagraphFormat->{Alignment} = $align;
356            
357             $range->ActionSettings(
358             $self->c->MouseClick
359             )->Hyperlink->{Address} = $options->{link} if $options->{link};
360             }
361            
362             sub DESTROY {
363             my $self = shift;
364            
365             $self->quit if $self->{was_invoked};
366             }
367            
368             1;
369             __END__