File Coverage

blib/lib/PDF/Template.pm
Criterion Covered Total %
statement 21 23 91.3
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 29 31 93.5


line stmt bran cond sub pod time code
1             package PDF::Template;
2              
3 1     1   50937 use strict;
  1         1  
  1         35  
4              
5             BEGIN {
6 1     1   541 use PDF::Template::Base;
  1         3  
  1         33  
7 1     1   6 use vars qw ($VERSION @ISA);
  1         2  
  1         60  
8              
9 1     1   3 $VERSION = '0.18';
10 1         31 @ISA = qw (PDF::Template::Base);
11             }
12              
13 1     1   1262 use PDF::Writer;
  1         276  
  1         5  
14              
15 1     1   25 use File::Basename;
  1         1  
  1         122  
16 1     1   837 use IO::File;
  1         26570  
  1         155  
17 1     1   504 use XML::Parser;
  0            
  0            
18              
19             #-----------------------------------------------
20             # TODO
21             #-----------------------------------------------
22             # PDF_set_info - find out more about this
23             # Providers - I need to create some provider classes that abstract
24             # the process of PDF creation. This will enable P::T to work with
25             # different PDF providers. A provider could be passed in to the
26             # constructor. If non is passed, P::T should try to instantiate a
27             # sensible provider depending on what is installed.
28             #-----------------------------------------------
29              
30             sub new
31             {
32             my $class = shift;
33             my $self = $class->SUPER::new(@_);
34              
35             $self->{TEMPLATES} = [] unless UNIVERSAL::isa($self->{TEMPLATES}, 'ARRAY');
36             $self->{PARAM_MAP} = {} unless UNIVERSAL::isa($self->{PARAM_MAP}, 'HASH');
37              
38             $self->{PDF_VERSION} = 0;
39              
40             $self->_validate_option($_)
41             for qw(OPENACTION OPENMODE);
42              
43             $self->parse_xml($self->{FILENAME}) if defined $self->{FILENAME};
44              
45             return $self;
46             }
47              
48             sub param
49             {
50             my $self = shift;
51              
52             # Allow an arbitrary number of hashrefs, so long as they're the first things
53             # into param(). Put each one onto the end, de-referenced.
54             push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
55              
56             (@_ % 2)
57             && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
58              
59             my %params = @_;
60             $params{uc $_} = delete $params{$_} for keys %params;
61             @{$self->{PARAM_MAP}}{keys %params} = @params{keys %params};
62              
63             return 1;
64             }
65              
66             sub write_file
67             {
68             my $self = shift;
69             my ($fname) = @_;
70              
71             my $p = PDF::Writer->new;
72             $p->open($fname) or die "Could not open file '$fname'.", $/;
73              
74             $self->_prepare_output($p);
75              
76             $p->save;
77              
78             return 1;
79             }
80              
81             sub output
82             {
83             my $self = shift;
84              
85             $self->get_buffer(@_);
86             }
87              
88             sub get_buffer
89             {
90             my $self = shift;
91              
92             my $p = PDF::Writer->new;
93             $p->open or die "Could not open buffer.", $/;
94              
95             $self->_prepare_output($p);
96              
97             return $p->stringify;
98             }
99              
100             sub parse
101             {
102             my $self = shift;
103              
104             $self->parse_xml(@_);
105             }
106              
107             sub parse_xml
108             {
109             my $self = shift;
110             my ($fname) = @_;
111              
112             my %Has_TextObject = map { $_ => undef } qw(
113             BOOKMARK
114             IMAGE
115             TEXTBOX
116             );
117              
118             my ($filename, $dirname) = fileparse($fname);
119            
120             my @stack;
121             my $parser = XML::Parser->new(
122             Base => $dirname,
123             Handlers => {
124             Start => sub {
125             shift;
126             # { local $"="', '"; print "Start: '@_'\n"; }
127             my $name = uc shift;
128              
129             # Pass the PDF encoding in.
130             if ($name eq 'PDFTEMPLATE')
131             {
132             if (exists $self->{PDF_ENCODING})
133             {
134             push @_, (
135             PDF_ENCODING => $self->{PDF_ENCODING},
136             );
137             }
138             }
139              
140             my $node = PDF::Template::Factory->create_node($name, @_);
141             die "'$name' (@_) didn't make a node!\n" unless defined $node;
142              
143             if ($name eq 'VAR')
144             {
145             return unless @stack;
146              
147             if (exists $stack[-1]{TXTOBJ} && $stack[-1]{TXTOBJ}->isa('TEXTOBJECT'))
148             {
149             push @{$stack[-1]{TXTOBJ}{STACK}}, $node;
150             }
151             }
152             elsif ($name eq 'PDFTEMPLATE')
153             {
154             push @{$self->{TEMPLATES}}, $node;
155             }
156             else
157             {
158             push @{$stack[-1]{ELEMENTS}}, $node
159             if @stack;
160             }
161              
162             push @stack, $node;
163             # print "Pushed $node onto stack\n";
164             },
165             Char => sub {
166             shift;
167             # { local $"="', '"; print "Char: '@_'\n"; }
168             return unless @stack;
169              
170             my $parent = $stack[-1];
171             if (exists $parent->{TXTOBJ} && $parent->{TXTOBJ}->isa('TEXTOBJECT'))
172             {
173             # print "Added '@_' to TextObject stack for '$parent'\n";
174             push @{$parent->{TXTOBJ}{STACK}}, @_;
175             }
176             },
177             End => sub {
178             shift;
179             # { local $"="', '"; print "End: '@_'\n"; }
180             return unless @stack;
181              
182             pop @stack if $stack[-1]->isa(uc $_[0]);
183             },
184             },
185             );
186              
187             {
188             my $fh = IO::File->new($fname)
189             || die "Cannot open '$fname' for reading: $!\n";
190            
191             $parser->parse(do { local $/ = undef; <$fh> });
192            
193             $fh->close;
194             }
195              
196             return 1;
197             }
198              
199             my %NoSetProperty = (
200             'CreationDate' => 1,
201             'Producer' => 1,
202             'ModDate' => 1,
203             'Trapped' => 1,
204             );
205              
206             sub _prepare_output
207             {
208             my $self = shift;
209             my ($p) = @_;
210              
211             $p->parameter('openaction' => $self->{OPENACTION});
212             $p->parameter('openmode' => $self->{OPENMODE});
213              
214             if (UNIVERSAL::isa($self->{INFO}, 'HASH'))
215             {
216             foreach my $key ( keys %{$self->{INFO}} )
217             {
218             if ($NoSetProperty{$key})
219             {
220             warn "Document property '$key' cannot be set.", $/;
221             next;
222             }
223              
224             $p->info($key, $self->{INFO}{$key});
225             }
226             }
227             else
228             {
229             $p->info($_, __PACKAGE__) for qw/Creator Author/;
230             }
231              
232             # __PAGE__ is incremented after the page is done.
233             $self->{PARAM_MAP}{__PAGE__} = 1;
234              
235             # __PAGEDEF__ is incremented when the pagedef begins.
236             $self->{PARAM_MAP}{__PAGEDEF__} = 0;
237              
238             my $context = PDF::Template::Factory->create(
239             'CONTEXT',
240             # Un-scoped variables
241             X => 0,
242             Y => 0,
243              
244             # Other variables
245             PDF => $p,
246             PARAM_MAP => [ $self->{PARAM_MAP} ],
247              
248             PDF_VERSION => $self->{PDF_VERSION},
249             DIE_ON_NO_PARAM => $self->{DIE_ON_NO_PARAM},
250             );
251              
252             # Do a first pass through, noting important values
253             $_->preprocess($context) for @{$self->{TEMPLATES}};
254              
255             # Do a second pass through, for actual rendering
256             $_->render($context) for @{$self->{TEMPLATES}};
257              
258             $context->close_images;
259              
260             return 1;
261             }
262              
263             sub register { shift; PDF::Template::Factory::register(@_) }
264              
265             1;
266             __END__