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 3     3   109919 use strict;
  3         7  
  3         124  
4              
5             BEGIN {
6 3     3   1354 use PDF::Template::Base;
  3         8  
  3         91  
7 3     3   19 use vars qw ($VERSION @ISA);
  3         5  
  3         161  
8              
9 3     3   6 $VERSION = '0.22';
10 3         87 @ISA = qw (PDF::Template::Base);
11             }
12              
13 3     3   949 use pdflib_pl;
  3         15905  
  3         86  
14              
15 3     3   23 use File::Basename;
  3         4  
  3         347  
16 3     3   15 use IO::File;
  3         6  
  3         371  
17 3     3   3368 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             for my $version (reverse 1 .. 6)
40             {
41             eval "UNIVERSAL::VERSION('pdflib_pl', $version.0)";
42             unless ($@)
43             {
44             $self->{PDF_VERSION} = $version;
45             last;
46             }
47             }
48             die "Cannot find pdflib_pl version",$/ unless $self->{PDF_VERSION};
49              
50             $self->_validate_option($_)
51             for qw(OPENACTION OPENMODE);
52              
53             $self->parse_xml($self->{FILENAME}) if defined $self->{FILENAME};
54              
55             return $self;
56             }
57              
58             sub param
59             {
60             my $self = shift;
61              
62             # Allow an arbitrary number of hashrefs, so long as they're the first things
63             # into param(). Put each one onto the end, de-referenced.
64             push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
65              
66             (@_ % 2)
67             && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
68              
69             my %params = @_;
70             $params{uc $_} = delete $params{$_} for keys %params;
71             @{$self->{PARAM_MAP}}{keys %params} = @params{keys %params};
72              
73             return 1;
74             }
75              
76             sub write_file
77             {
78             my $self = shift;
79             my ($fname) = @_;
80              
81             my $p = pdflib_pl::PDF_new();
82              
83             pdflib_pl::PDF_open_file($p, $fname) == -1
84             && die "pdflib_pl::PDF_open_file could not open file '$fname'.", $/;
85              
86             $self->_prepare_output($p);
87              
88             pdflib_pl::PDF_close($p);
89              
90             return 1;
91             }
92              
93             sub output
94             {
95             my $self = shift;
96              
97             $self->get_buffer(@_);
98             }
99              
100             sub get_buffer
101             {
102             my $self = shift;
103              
104             my $p = pdflib_pl::PDF_new();
105              
106             pdflib_pl::PDF_open_file($p, '') == -1
107             && die "pdflib_pl::PDF_open_file could not open buffer.", $/;
108              
109             $self->_prepare_output($p);
110              
111             pdflib_pl::PDF_close($p);
112              
113             return pdflib_pl::PDF_get_buffer($p);
114             }
115              
116             sub parse
117             {
118             my $self = shift;
119              
120             $self->parse_xml(@_);
121             }
122              
123             sub parse_xml
124             {
125             my $self = shift;
126             my ($fname) = @_;
127              
128             my %Has_TextObject = map { $_ => undef } qw(
129             BOOKMARK
130             IMAGE
131             TEXTBOX
132             );
133              
134             my ($filename, $dirname) = fileparse($fname);
135            
136             my @stack;
137             my $parser = XML::Parser->new(
138             Base => $dirname,
139             Handlers => {
140             Start => sub {
141             shift;
142             # { local $"="', '"; print "Start: '@_'\n"; }
143             my $name = uc shift;
144              
145             # Pass the PDF encoding in.
146             if ($name eq 'PDFTEMPLATE')
147             {
148             if (exists $self->{PDF_ENCODING})
149             {
150             push @_, (
151             PDF_ENCODING => $self->{PDF_ENCODING},
152             );
153             }
154             }
155              
156             my $node = PDF::Template::Factory->create_node($name, @_);
157             die "'$name' (@_) didn't make a node!\n" unless defined $node;
158              
159             if ($name eq 'VAR')
160             {
161             return unless @stack;
162              
163             if (exists $stack[-1]{TXTOBJ} && $stack[-1]{TXTOBJ}->isa('TEXTOBJECT'))
164             {
165             push @{$stack[-1]{TXTOBJ}{STACK}}, $node;
166             }
167             }
168             elsif ($name eq 'PDFTEMPLATE')
169             {
170             push @{$self->{TEMPLATES}}, $node;
171             }
172             else
173             {
174             push @{$stack[-1]{ELEMENTS}}, $node
175             if @stack;
176             }
177              
178             push @stack, $node;
179             # print "Pushed $node onto stack\n";
180             },
181             Char => sub {
182             shift;
183             # { local $"="', '"; print "Char: '@_'\n"; }
184             return unless @stack;
185              
186             my $parent = $stack[-1];
187             if (exists $parent->{TXTOBJ} && $parent->{TXTOBJ}->isa('TEXTOBJECT'))
188             {
189             # print "Added '@_' to TextObject stack for '$parent'\n";
190             push @{$parent->{TXTOBJ}{STACK}}, @_;
191             }
192             },
193             End => sub {
194             shift;
195             # { local $"="', '"; print "End: '@_'\n"; }
196             return unless @stack;
197              
198             pop @stack if $stack[-1]->isa(uc $_[0]);
199             },
200             },
201             );
202              
203             {
204             my $fh = IO::File->new($fname)
205             || die "Cannot open '$fname' for reading: $!\n";
206            
207             $parser->parse(do { local $/ = undef; <$fh> });
208            
209             $fh->close;
210             }
211              
212             return 1;
213             }
214              
215             my %NoSetProperty = (
216             'CreationDate' => 1,
217             'Producer' => 1,
218             'ModDate' => 1,
219             'Trapped' => 1,
220             );
221              
222             sub _prepare_output
223             {
224             my $self = shift;
225             my ($p) = @_;
226              
227             pdflib_pl::PDF_set_parameter($p, 'openaction', $self->{OPENACTION});
228             pdflib_pl::PDF_set_parameter($p, 'openmode', $self->{OPENMODE});
229              
230             if (UNIVERSAL::isa($self->{INFO}, 'HASH'))
231             {
232             foreach my $key ( keys %{$self->{INFO}} )
233             {
234             if ($NoSetProperty{$key})
235             {
236             warn "Document property '$key' cannot be set.", $/;
237             next;
238             }
239              
240             pdflib_pl::PDF_set_info($p, $key, $self->{INFO}{$key});
241             }
242             }
243             else
244             {
245             pdflib_pl::PDF_set_info($p, $_, __PACKAGE__) for qw/Creator Author/;
246             }
247              
248             # __PAGE__ is incremented after the page is done.
249             $self->{PARAM_MAP}{__PAGE__} = 1;
250              
251             # __PAGEDEF__ is incremented when the pagedef begins.
252             $self->{PARAM_MAP}{__PAGEDEF__} = 0;
253              
254             my $context = PDF::Template::Factory->create(
255             'CONTEXT',
256             # Un-scoped variables
257             X => 0,
258             Y => 0,
259              
260             # Other variables
261             PDF => $p,
262             PARAM_MAP => [ $self->{PARAM_MAP} ],
263              
264             PDF_VERSION => $self->{PDF_VERSION},
265             DIE_ON_NO_PARAM => $self->{DIE_ON_NO_PARAM},
266             );
267              
268             # Do a first pass through, noting important values
269             $_->preprocess($context) for @{$self->{TEMPLATES}};
270              
271             # Do a second pass through, for actual rendering
272             $_->render($context) for @{$self->{TEMPLATES}};
273              
274             $context->close_images;
275              
276             return 1;
277             }
278              
279             sub register { shift; PDF::Template::Factory::register(@_) }
280              
281             1;
282             __END__