File Coverage

blib/lib/PDF/Template.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


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