File Coverage

blib/lib/PDF/FDF/Simple.pm
Criterion Covered Total %
statement 104 115 90.4
branch 19 28 67.8
condition 1 3 33.3
subroutine 17 18 94.4
pod 4 4 100.0
total 145 168 86.3


line stmt bran cond sub pod time code
1             package PDF::FDF::Simple;
2              
3 15     15   229255 use strict;
  15         36  
  15         825  
4 15     15   83 use warnings;
  15         30  
  15         560  
5              
6 15     15   117 use vars qw($VERSION $deferred_result_FDF_OPTIONS);
  15         34  
  15         977  
7 15     15   25142 use Data::Dumper;
  15         266942  
  15         1351  
8 15     15   29967 use Parse::RecDescent;
  15         900884  
  15         131  
9 15     15   16723 use IO::File;
  15         38943  
  15         2533  
10              
11 15     15   161 use base 'Class::Accessor::Fast';
  15         37  
  15         15531  
12             PDF::FDF::Simple->mk_accessors(qw(
13             skip_undefined_fields
14             filename
15             content
16             errmsg
17             parser
18             attribute_file
19             attribute_ufile
20             attribute_id
21             ));
22              
23             $VERSION = '0.21';
24              
25             #Parse::RecDescent environment variables: enable for Debugging
26             #$::RD_TRACE = 1;
27             #$::RD_HINT = 1;
28              
29             sub new {
30 25     25 1 211151 my $class = shift;
31              
32 25         63 my $parser;
33 25 50       143 if ($ENV{PDF_FDF_SIMPLE_IGNORE_PRECOMPILED_GRAMMAR}) {
34             # use external grammar file
35 0         0 require File::ShareDir;
36 0         0 my $grammar_file = File::ShareDir::module_file('PDF::FDF::Simple', 'grammar');
37 0 0       0 open GRAMMAR_FILE, $grammar_file or die "Cannot open grammar file ".$grammar_file;
38 0         0 local $/;
39 0         0 my $grammar = ;
40 0         0 $parser = Parse::RecDescent->new($grammar);
41             } else {
42             # use precompiled grammar
43 25         78579 require PDF::FDF::Simple::Grammar;
44 25         274 $parser = new PDF::FDF::Simple::Grammar;
45             }
46              
47 25         941 my %DEFAULTS = (
48             errmsg => '',
49             skip_undefined_fields => 0,
50             parser => $parser
51             );
52             # accept hashes or hash refs for backwards compatibility
53 25 100       394 my %ARGS = ref($_[0]) =~ /HASH/ ? %{$_[0]} : @_;
  20         180  
54 25         329 my $self = Class::Accessor::new($class, { %DEFAULTS, %ARGS });
55 25         689 return $self;
56             }
57              
58             sub _fdf_header {
59 5     5   12 my $self = shift;
60              
61 5         15 my $string = "%FDF-1.2\n\n1 0 obj\n<<\n/FDF << /Fields 2 0 R";
62             # /F
63 5 100       23 if ($self->attribute_file){
64 2         20 $string .= "/F (".$self->attribute_file.")";
65             }
66             # /UF
67 5 100       69 if ($self->attribute_ufile){
68 2         18 $string .= "/UF (".$self->attribute_ufile.")";
69             }
70             # /ID
71 5 100       59 if ($self->attribute_id){
72 2         22 $string .= "/ID[";
73 2         4 $string .= $_ foreach @{$self->attribute_id};
  2         7  
74 2         17 $string .= "]";
75             }
76 5         32 $string .= ">>\n>>\nendobj\n2 0 obj\n[";
77 5         36 return $string;
78             }
79              
80             sub _fdf_footer {
81 5     5   12 my $self = shift;
82 5         14 return <<__EOT__;
83             ]
84             endobj
85             trailer
86             <<
87             /Root 1 0 R
88              
89             >>
90             %%EOF
91             __EOT__
92             }
93              
94             sub _quote {
95 52     52   65 my $self = shift;
96 52         64 my $str = shift;
97 52         80 $str =~ s,\\,\\\\,g;
98 52         72 $str =~ s,\(,\\(,g;
99 52         74 $str =~ s,\),\\),g;
100 52         67 $str =~ s,\n,\\r,gs;
101 52         207 return $str;
102             }
103              
104             sub _fdf_field_formatstr {
105 52     52   64 my $self = shift;
106 52         140 return "<< /T (%s) /V (%s) >>\n"
107             }
108              
109             sub as_string {
110 5     5 1 785 my $self = shift;
111 5         27 my $fdf_string = $self->_fdf_header;
112 5         12 foreach (sort keys %{$self->content}) {
  5         27  
113 53         210 my $val = $self->content->{$_};
114 53 100       292 if (not defined $val) {
115 2 100       9 next if ($self->skip_undefined_fields);
116 1         9 $val = '';
117             }
118 52         107 $fdf_string .= sprintf ($self->_fdf_field_formatstr,
119             $_,
120             $self->_quote($val));
121             }
122 5         29 $fdf_string .= $self->_fdf_footer;
123 5         75 return $fdf_string;
124             }
125              
126             sub save {
127 4     4 1 1809 my $self = shift;
128 4   33     40 my $filename = shift || $self->filename;
129 4 50       625 open (F, "> ".$filename) or do {
130 0         0 $self->errmsg ('error: open file ' . $filename);
131 0         0 return 0;
132             };
133              
134 4         59 print F $self->as_string;
135 4         456 close (F);
136              
137 4         23 $self->errmsg ('');
138 4         42 return 1;
139             }
140              
141             sub _read_fdf {
142 16     16   45 my $self = shift;
143 16         38 my $filecontent;
144              
145             # read file to be checked
146 16 50       162 unless (open FH, "< ".$self->filename) {
147 0         0 $self->errmsg ('error: could not read file ' . $self->filename);
148 0         0 return undef;
149             } else {
150 16         1450 local $/;
151 16         677 $filecontent = ;
152             }
153 16         252 close FH;
154 16         100 $self->errmsg ('');
155 16         148 return $filecontent;
156             }
157              
158             sub _map_parser_output {
159 17     17   37 my $self = shift;
160 17         32 my $output = shift;
161              
162 17         36 my $fdfcontent = {};
163 17         53 foreach my $obj ( @$output ) {
164 21         51 foreach my $contentblock ( @$obj ) {
165 699         1716 foreach my $keys (keys %$contentblock) {
166 699         2491 $fdfcontent->{$keys} = $contentblock->{$keys};
167             }
168             }
169             }
170 17         115 return $fdfcontent;
171             }
172              
173             sub load {
174 17     17 1 3915 my $self = shift;
175 17         46 my $filecontent = shift;
176              
177             # prepare content
178 17 100       104 unless ($filecontent) {
179 16         94 $filecontent = $self->_read_fdf;
180 16 50       83 return undef unless $filecontent;
181             }
182              
183             # parse
184 17         41 my $output;
185             {
186 17 0   0   40 local $SIG{'__WARN__'} = sub { warn $_[0] unless $_[0] =~ /Deep recursion on subroutine/ };
  17         166  
  0         0  
187 17         107 $output = $self->parser->startrule ($filecontent);
188             }
189              
190             # take over parser results
191 17         570 $self->attribute_file ($PDF::FDF::Simple::deferred_result_FDF_OPTIONS->{F}); # /F
192 17         219 $self->attribute_ufile ($PDF::FDF::Simple::deferred_result_FDF_OPTIONS->{UF}); # /UF
193 17         174 $self->attribute_id ($PDF::FDF::Simple::deferred_result_FDF_OPTIONS->{ID}); # /ID
194 17         154 $self->content ($self->_map_parser_output ($output));
195 17 50       155 $self->errmsg ("Corrupt FDF file!\n") unless $self->content;
196              
197 17         181 return $self->content;
198             }
199              
200             1;