File Coverage

blib/lib/CAM/PDFTaxforms.pm
Criterion Covered Total %
statement 207 271 76.3
branch 89 174 51.1
condition 15 45 33.3
subroutine 9 10 90.0
pod 5 5 100.0
total 325 505 64.3


line stmt bran cond sub pod time code
1             package CAM::PDFTaxforms;
2              
3 4     4   145507 use 5.006;
  4         40  
4 4     4   24 use warnings;
  4         8  
  4         120  
5 4     4   20 use strict;
  4         8  
  4         98  
6 4     4   1767 use parent 'CAM::PDF';
  4         1265  
  4         20  
7              
8             our $VERSION = '1.20';
9              
10             =head1 NAME
11              
12             CAM::PDFTaxforms - CAM::PDF wrapper to also allow editing of checkboxes (ie. for IRS Tax forms).
13              
14             =head1 AUTHOR
15              
16             Jim Turner C<< >>.
17              
18             This module is a wrapper around and a drop-in replacement for
19             L, by Chris Dolan.
20              
21             =head1 ACKNOWLEDGMENTS
22              
23             Thanks to Chris Dolan and everyone involved in developing and
24             supporting CAM::PDF, on which this module is based and relies on.
25              
26             =head1 LICENSE AND COPYRIGHT
27              
28             Copyright (c) 2010-2019 Jim Turner C<< >>
29              
30             This library is free software; you can redistribute it and/or modify it
31             under the same terms as CAM::PDF and Perl itself.
32              
33             L:
34              
35             Copyright (c) 2002-2006 Clotho Advanced Media, Inc., L
36              
37             Copyright (c) 2007-2008 Chris Dolan
38              
39             This library is free software; you can redistribute it and/or modify it
40             under the same terms as Perl itself.
41              
42             =head1 SYNOPSIS
43              
44             #!/usr/bin/perl -w
45              
46             use strict;
47             use CAM::PDFTaxforms;
48             my $pdf = CAM::PDFTaxforms->new('f1040.pdf') or die "Could not open PDF ($!)!";
49             my $page1 = $pdf->getPageContent(1);
50              
51             #DISPLAY THE LIST NAMES OF EDITABLE FIELDS:
52             my @fieldnames = $pdf->getFormFieldList();
53             print "--fields=".join('|',@fieldnames)."=\n";
54              
55             #UPDATE THE VALUES OF ONE OF THE FIELDS AND A COUPLE OF THE CHECKBOXES:
56             $pdf->fillFormFields('fieldname1' => 'value1', 'fieldname2' => 'value2');
57              
58             #WRITE THE UPDATED PDF FORM TO A NEW FILE NAME:
59             $pdf->cleanoutput('f1040_completed.pdf');
60              
61             Many example programs are included in this distribution to do useful
62             tasks. See the C subdirectory.
63              
64             =head1 DESCRIPTION
65              
66             This package is a wrapper for and creates a L object. The
67             difference is that some method functions are overridden to fix some
68             issues and add some new features, namely to better handle IRS tax
69             forms, many of which have checkboxes, in addition to numeric and text
70             fields. Several other patches have also been applied, particularly
71             those provided by CAM::PDF bugs #58144, #122890 and #125299.
72             Otherwise, it should work well as a full drop-in replacement for
73             CAM::PDF in the API.
74              
75             CAM::PDF description:
76              
77             This package reads and writes any document that conforms to the PDF
78             specification generously provided by Adobe at
79             L
80             (link last checked Oct 2005).
81              
82             The file format through PDF 1.5 is well-supported, with the exception
83             of the "linearized" or "optimized" output format, which this module
84             can read but not write. Many specific aspects of the document model
85             are not manipulable with this package (like fonts), but if the input
86             document is correctly written, then this module will preserve the
87             model integrity.
88              
89             The PDF writing feature saves as PDF 1.4-compatible. That means that
90             we cannot write compressed object streams. The consequence is that
91             reading and then writing a PDF 1.5+ document may enlarge the resulting
92             file by a fair margin.
93              
94             This library grants you some power over the PDF security model. Note
95             that applications editing PDF documents via this library MUST respect
96             the security preferences of the document. Any violation of this
97             respect is contrary to Adobe's intellectual property position, as
98             stated in the reference manual at the above URL.
99              
100             Technical detail regarding corrupt PDFs: This library adheres strictly
101             to the PDF specification. Adobe's Acrobat Reader is more lenient,
102             allowing some corrupted PDFs to be viewable. Therefore, it is
103             possible that some PDFs may be readable by Acrobat that are illegible
104             to this library. In particular, files which have had line endings
105             converted to or from DOS/Windows style (i.e. CR-NL) may be rendered
106             unusable even though Acrobat does not complain. Future library
107             versions may relax the parser, but not yet.
108              
109             This version is HACKED by Jim Turner 09/2010 to enable the fillFormFields()
110             function to also modify checkboxes (primarily on IRS Tax forms).
111              
112             =head1 EXAMPLES
113              
114             See the I subdirectory in the source tree. There is a sample
115             blank 2018 official IRS Schedule B tax form and two programs:
116             I, which fills in the form using the sample input data
117             text file I, and creates a filled in version of the
118             form called I. The other program (I)
119             can read the data filled in the filled in form created by the other
120             program and displays it as output.
121              
122             To run the programs, switch to the I subdirectory in the source
123             tree and run them without arguments (ie. B<./dof1040sb.pl>).
124              
125             To see the names of the fields and their current values in a PDF form,
126             such as the aforementioned tax form, run the included program, ie:
127             I.
128              
129             =head1 API
130              
131             =head2 Functions intended to be used externally
132              
133             $self = CAM::PDFTaxforms->new(content | filename | '-')
134             $self->toPDF()
135             $self->needsSave()
136             $self->save()
137             $self->cleansave()
138             $self->output(filename | '-')
139             $self->cleanoutput(filename | '-')
140             $self->previousRevision()
141             $self->allRevisions()
142             $self->preserveOrder()
143             $self->appendObject(olddoc, oldnum, [follow=(1|0)])
144             $self->replaceObject(newnum, olddoc, oldnum, [follow=(1|0)])
145             (olddoc can be undef in the above for adding new objects)
146             $self->numPages()
147             $self->getPageText(pagenum)
148             $self->getPageDimensions(pagenum)
149             $self->getPageContent(pagenum)
150             $self->setPageContent(pagenum, content)
151             $self->appendPageContent(pagenum, content)
152             $self->deletePage(pagenum)
153             $self->deletePages(pagenum, pagenum, ...)
154             $self->extractPages(pagenum, pagenum, ...)
155             $self->appendPDF(CAM::PDF object)
156             $self->prependPDF(CAM::PDF object)
157             $self->wrapString(string, width, fontsize, page, fontlabel)
158             $self->getFontNames(pagenum)
159             $self->addFont(page, fontname, fontlabel, [fontmetrics])
160             $self->deEmbedFont(page, fontname, [newfontname])
161             $self->deEmbedFontByBaseName(page, basename, [newfont])
162             $self->getPrefs()
163             $self->setPrefs()
164             $self->canPrint()
165             $self->canModify()
166             $self->canCopy()
167             $self->canAdd()
168             $self->getFormFieldList()
169             $self->fillFormFields(fieldname, value, [fieldname, value, ...])
170             or $self->fillFormFields(%values)
171             $self->clearFormFieldTriggers(fieldname, fieldname, ...)
172              
173             Note: 'clean' as in cleansave() and cleanobject() means write a fresh
174             PDF document. The alternative (e.g. save()) reuses the existing doc
175             and just appends to it. Also note that 'clean' functions sort the
176             objects numerically. If you prefer that the new PDF docs more closely
177             resemble the old ones, call preserveOrder() before cleansave() or
178             cleanobject().
179              
180             =head2 For additional methods and functions, see the L documentation.
181              
182             =head1 METHODS
183              
184             =over
185              
186             =item $doc = CAM::PDFTaxforms->new($content)
187              
188             =item $doc = CAM::PDFTaxforms->new($ownerpass, $userpass)
189              
190             =item $doc = CAM::PDFTaxforms->new($content, $ownerpass, $userpass, $prompt)
191              
192             =item $doc = CAM::PDFTaxforms->new($content, $ownerpass, $userpass, $options)
193              
194             Instantiate a new CAM::PDFTaxforms object. C<$content> can be a document
195             in a string, a filename, or '-'. The latter indicates that the document
196             should be read from standard input. If the document is password
197             protected, the passwords should be passed as additional arguments. If
198             they are not known, a boolean C<$prompt> argument allows the programmer to
199             suggest that the constructor prompt the user for a password. This is
200             rudimentary prompting: passwords are in the clear on the console.
201              
202             This constructor takes an optional final argument which is a hash
203             reference. This hash can contain any of the following optional
204             parameters:
205              
206             =over
207              
208             =item prompt_for_password => $boolean
209              
210             This is the same as the C<$prompt> argument described above.
211              
212             =item fault_tolerant => $boolean
213              
214             This flag causes the instance to be more lenient when reading the
215             input PDF. Currently, this only affects PDFs which cannot be
216             successfully decrypted.
217              
218             =back
219              
220             =item $hashref = $doc->getFieldValue('fieldname1' [, fieldname2, ... fieldnameN ])
221              
222             (CAM::PDFTaxforms only, not available in CAM::PDF)
223              
224             Fetches the corresponding current values for each field name in the
225             argument list. Returns a reference to a hash containing the field
226             names as keys and the corresponding values. If a field does not
227             exist or does not contain a value, an empty string is returned in
228             the hash as it's value. If called in array / hash context, then
229             a list of field names and values in the order (fieldname1, value1,
230             fieldname2, value2, ... fieldnameN valueN) is returned.
231              
232             =cut
233              
234             sub getFieldValue #JWT:NEW FUNCTION ADDED 20100921 TO RETURN CORRECT VALUES
235             #FOR EACH FIELD WHETHER IT'S A TEXT FIELD OR A CHECKBOX:
236             {
237 1     1 1 18 my $self = shift;
238 1         6 my @fieldNames = @_;
239              
240 1         3 my ($objnode, $propdict, $dict, $fieldType, $fieldHashRef);
241 1         4 LOOP1: foreach my $fieldName (@fieldNames)
242             {
243 3         11 $objnode = $self->getFormField($fieldName);
244 3         8 $fieldHashRef->{$fieldName} = undef;
245 3 50       10 next LOOP1 unless ($objnode);
246              
247             # This read-only dict includes inherited properties
248 3         21 my $propdict = $self->getFormFieldDict($objnode);
249              
250             # This read-write dict does not include inherited properties
251 3         2218 my $dict = $self->getValue($objnode);
252              
253 3 100 66     56 if ($propdict->{FT} && $self->getValue($propdict->{FT}) =~ /^Btn$/o) {
254             $fieldHashRef->{$fieldName} = (defined $dict->{AS}->{value})
255 1 50       20 ? $dict->{AS}->{value} : $dict->{V}->{value};
256             } else {
257 2         34 $fieldHashRef->{$fieldName} = $dict->{V}->{value};
258             }
259             }
260 1 50       6 return $fieldHashRef unless (wantarray); #RETURN HASHREF IN SCALAR CONTEXT.
261 1         3 my @fieldValues;
262 1         3 foreach my $fieldName (@fieldNames) #BUILD ARRAY FROM HASH TO RETURN IN ARRAY CONTEXT:
263             {
264 3         9 push (@fieldValues, $fieldName, $fieldHashRef->{$fieldName});
265             }
266 1         7 return @fieldValues;
267             }
268              
269             =item $doc->fillFormFields($name => $value, ...)
270              
271             =item $doc->fillFormFields($opts_hash, $name => $value, ...)
272              
273             Set the default values of PDF form fields. The name should be the
274             full hierarchical name of the field as output by the
275             getFormFieldList() function. The argument list can be a hash if you
276             like. A simple way to use this function is something like this:
277              
278             my %fields = (fname => 'John', lname => 'Smith', state => 'WI');
279             $field{zip} = 53703;
280             $self->fillFormFields(%fields);
281              
282             NOTE: For checkbox fields specify any value that is I in Perl
283             (ie. 0, '', or I), or any of the strings: 'Off', 'No', or
284             'Unchecked' (case insensitive) to un-check a checkbox, or any other
285             value that is I in Perl to check it. Checkbox fields are only
286             supported by CAM::PDFTaxforms and was the original reason for
287             creating it.
288              
289             If the first argument is a hash reference, it is interpreted as
290             options for how to render the filled data:
291              
292             =over
293              
294             =item background_color =E 'none' | $gray | [$r, $g, $b]
295              
296             Specify the background color for the text field.
297              
298             =back
299              
300             =cut
301              
302             sub fillFormFields ## no critic(Subroutines::ProhibitExcessComplexity, Unpack)
303             {
304 12     12 1 1963 my $self = shift;
305 12 50       34 my $opts = ref $_[0] ? shift : {};
306 12         48 my @list = (@_);
307              
308             my %opts = (
309             background_color => 1,
310 12         23 %{$opts},
  12         44  
311             );
312              
313 12         22 my $filled = 0;
314 12         31 LOOP1: while (@list > 0)
315             {
316 25         48 my $key = shift @list;
317 25         49 my $value = shift @list;
318              
319 25 50       57 $value = q{} unless (defined $value);
320 25 50 33     116 next if (!$key || ref($key));
321              
322 25         70 my $objnode = $self->getFormField($key);
323 25 50       70 next unless ($objnode);
324              
325 25         75 my $objnum = $objnode->{objnum};
326 25         54 my $gennum = $objnode->{gennum};
327              
328             # This read-only dict includes inherited properties
329 25         85 my $propdict = $self->getFormFieldDict($objnode);
330              
331             # This read-write dict does not include inherited properties
332 25         23798 my $dict = $self->getValue($objnode);
333 25         394 $dict->{V} = CAM::PDF::Node->new('string', $value, $objnum, $gennum);
334              
335 25 50 33     427 if ($propdict->{FT} && $self->getValue($propdict->{FT}) =~ /^(Tx|Btn)$/o) # Is it a text field?
336             {
337 25         354 my $fieldType = $1; #JWT:ADDED NEXT 6 TO ALLOW SETTING OF CHECKBOX BUTTONS (VALUE MUST BE EITHER "Yes" or "Off"!:
338 25 100       58 if ($fieldType eq 'Btn') #WE'RE A BUTTON (CHECKBOX OR RADIO)
339             {
340 2         21 my @kidnames = $self->getFormFieldList($key);
341 2 50       8 if (@kidnames > 0) { #WE HAVE KIDS, SO WE'RE A RADIO-BUTTON:
342             local * setRadioButtonKids = sub {
343 0     0   0 my ($indx, $vindx) = @_;
344 0         0 my $objnode = $self->getFormField($kidnames[$indx]);
345 0 0       0 return unless ($objnode);
346 0         0 my $dict = $self->getValue($objnode);
347 0 0       0 if ($indx == $vindx) {
348 0         0 $dict->{AS}->{value} = $value;
349             } else {
350 0         0 $dict->{AS}->{value} = 'Off';
351             }
352 0         0 return;
353 0         0 };
354              
355 0 0       0 $dict->{V}->{value} = ($value > 0) ? $value : 'Off';
356 0         0 my $vindx = $value - 1;
357 0         0 for (my $i=0;$i<=$#kidnames;$i++) {
358 0         0 &setRadioButtonKids($i, $vindx);
359             }
360             } else { #WE'RE A SINGLE CHECKBOX:
361 2 50 33     11 if (!$value || $value =~ /^(?:Off|No|Unchecked)$/io) { #USER WANTS IT UNCHECKED:
362 0         0 $dict->{AS}->{value} = 'Off';
363 0         0 $dict->{V}->{value} = 'Off';
364             } else { #USER WANTS IT CHECKED:
365             my ($onValue) = defined($dict->{AP}->{value}->{N}->{value}
366             && ref($dict->{AP}->{value}->{N}->{value}) =~ /^HASH/)
367 2 50 33     21 ? keys(%{$dict->{AP}->{value}->{N}->{value}}) : ('Yes');
  2         11  
368 2         7 $dict->{AS}->{value} = $onValue;
369 2         6 $dict->{V}->{value} = $onValue;
370             }
371             }
372 2         4 $filled++;
373 2         14 next LOOP1;
374             }
375             else #WE'RE A TEXT FIELD:
376             {
377 23         46 $dict->{V}->{value} = $value;
378             }
379              
380             # Set up display of form value
381 23 50       97 $dict->{AP} = CAM::PDF::Node->new('dictionary', {}, $objnum, $gennum) unless ($dict->{AP});
382 23 50       344 unless ($dict->{AP}->{value}->{N})
383             {
384 23         60 my $newobj = CAM::PDF::Node->new('object',
385             CAM::PDF::Node->new('dictionary',{}),
386             );
387 23         458 my $num = $self->appendObject(undef, $newobj, 0);
388 23         2510 $dict->{AP}->{value}->{N} = CAM::PDF::Node->new('reference', $num, $objnum, $gennum);
389             }
390             my $formobj = ($self->dereference($fieldType eq 'Btn' && defined($dict->{AS}->{value})
391             && $dict->{AS}->{value}
392             && defined($dict->{AP}->{value}->{N}->{value}->{$dict->{AS}->{value}}->{value})
393             ? $dict->{AP}->{value}->{N}->{value}->{$dict->{AS}->{value}}->{value}
394 23 50 0     458 : $dict->{AP}->{value}->{N}->{value}));
395 23         273 my $formonum = $formobj->{objnum};
396 23         52 my $formgnum = $formobj->{gennum};
397 23         53 my $formdict = $self->getValue($formobj);
398              
399             $formdict->{Subtype} = CAM::PDF::Node->new('label', 'Form', $formonum, $formgnum)
400 23 50       338 unless ($formdict->{Subtype});
401              
402 23         326 my @rect = (0,0,0,0);
403 23 50       81 if ($dict->{Rect})
404             {
405             ## no critic(Bangs::ProhibitNumberedNames)
406 23         55 my $r = $self->getValue($dict->{Rect});
407 23         250 my ($x1, $y1, $x2, $y2) = @{$r};
  23         76  
408 23         49 @rect = (
409             $self->getValue($x1),
410             $self->getValue($y1),
411             $self->getValue($x2),
412             $self->getValue($y2),
413             );
414             }
415 23         793 my $dx = $rect[2]-$rect[0];
416 23         55 my $dy = $rect[3]-$rect[1];
417 23 50       50 unless ($formdict->{BBox})
418             {
419 23         53 $formdict->{BBox} = CAM::PDF::Node->new('array',
420             [
421             CAM::PDF::Node->new('number', 0, $formonum, $formgnum),
422             CAM::PDF::Node->new('number', 0, $formonum, $formgnum),
423             CAM::PDF::Node->new('number', $dx, $formonum, $formgnum),
424             CAM::PDF::Node->new('number', $dy, $formonum, $formgnum),
425             ],
426             $formonum, $formgnum);
427             }
428 23         1365 my $text = $value;
429 23         47 $text =~ s/ \r\n? /\n/gxmso;
430 23         41 $text =~ s/ \n+\z //xmso;
431              
432 23         32 my @rsrcs;
433 23         38 my $fontmetrics = 0;
434 23         30 my $fontname = q{};
435 23         33 my $fontsize = 0;
436 23         31 my $da = q{};
437 23         34 my $tl = q{};
438             #JWT:CHGD TO NEXT PER BUG#122890: my $border = 2;
439 23         31 my $border = 1;
440 23         31 my $tx = $border;
441             #JWT:CHGD TO NEXT PER BUG#122890: my $ty = $border + 2;
442 23         33 my $ty = $border + 1;
443 23         33 my $stringwidth;
444 23 50       50 if ($propdict->{DA}) {
445 23         54 $da = $self->getValue($propdict->{DA});
446              
447             # Try to pull out all of the resources used in the text object
448 23         298 @rsrcs = ($da =~ m{ /([^\s<>/\[\]()]+) }gxmso);
449              
450             # Try to pull out the font size, if any. If more than
451             # one, pick the last one. Font commands look like:
452             # "/ Tf"
453             #JWT: CHGD. TO NEXT (BUG#58144 PATCH): if ($da =~ m{ \s*/(\w+)\s+(\d+)\s+Tf.*? \z }xms)
454 23 50       153 if ($da =~ m{ \s*/([\w-]+)\s+([.\d]+)\s+Tf.*? \z }xmso)
455             {
456 23         61 $fontname = $1;
457 23         41 $fontsize = $2;
458 23 50       46 if ($fontname)
459             {
460 23 50       63 if ($propdict->{DR})
461             {
462 0         0 my $dr = $self->getValue($propdict->{DR});
463 0         0 $fontmetrics = $self->getFontMetrics($dr, $fontname);
464             }
465             #print STDERR "Didn't get font\n" if (!$fontmetrics);
466             }
467             }
468             }
469              
470 23         91 my %flags = (
471             Justify => 'left',
472             );
473 23 100       52 if ($propdict->{Ff})
474             {
475             # Just decode the ones we actually care about
476             # PDF ref, 3rd ed pp 532,543
477 22         51 my $ff = $self->getValue($propdict->{Ff});
478 22         424 my @flags = split m//xms, unpack 'b*', pack 'V', $ff;
479 22         61 $flags{ReadOnly} = $flags[0];
480 22         42 $flags{Required} = $flags[1];
481 22         42 $flags{NoExport} = $flags[2];
482 22         37 $flags{Multiline} = $flags[12];
483 22         40 $flags{Password} = $flags[13];
484 22         35 $flags{FileSelect} = $flags[20];
485 22         38 $flags{DoNotSpellCheck} = $flags[22];
486 22         76 $flags{DoNotScroll} = $flags[23];
487             }
488 23 50       54 if ($propdict->{Q})
489             {
490 23   100     58 my $q = $self->getValue($propdict->{Q}) || 0;
491 23 100       259 $flags{Justify} = $q==2 ? 'right' : ($q==1 ? 'center' : 'left');
    100          
492             }
493              
494             # The order of the following sections is important!
495 23 50       51 $text =~ s/ [^\n] /*/gxms if ($flags{Password}); # Asterisks for password characters
496              
497 23 50 33     52 if ($fontmetrics && ! $fontsize)
498             {
499             # Fix autoscale fonts
500 0         0 $stringwidth = 0;
501 0         0 my $lines = 0;
502 0         0 for my $line (split /\n/xmso, $text) # trailing null strings omitted
503             {
504 0         0 $lines++;
505 0         0 my $w = $self->getStringWidth($fontmetrics, $line);
506 0 0 0     0 $stringwidth = $w if ($w && $w > $stringwidth);
507             }
508 0   0     0 $lines ||= 1;
509             # Initial guess
510 0         0 $fontsize = ($dy - 2 * $border) / ($lines * 1.5);
511 0         0 my $fontwidth = $fontsize * $stringwidth;
512 0         0 my $maxwidth = $dx - 2 * $border;
513 0 0       0 $fontsize *= $maxwidth / $fontwidth if ($fontwidth > $maxwidth);
514 0         0 $da =~ s/ \/$fontname\s+0\s+Tf\b /\/$fontname $fontsize Tf/gxms;
515             }
516 23 50       41 if ($fontsize)
517             {
518             # This formula is TOTALLY empirical. It's probably wrong.
519             # #JWT:CHGD. TO NEXT: $ty = $border + 2 + (9 - $fontsize) * 0.4;
520 23         74 $ty = $border + 2 + (5 - $fontsize) * 0.4;
521             }
522              
523              
524             # escape characters
525 23         77 $text = $self->writeString($text);
526              
527 23 50       683 if ($flags{Multiline})
528             {
529             # TODO: wrap the field with wrapString()??
530             # Shawn Dawson of Silent Solutions pointed out that this does not auto-wrap the input text
531              
532 0         0 my $linebreaks = $text =~ s/ \\n /\) Tj T* \(/gxms;
533              
534             # Total guess work:
535             # line height is either 150% of fontsize or thrice
536             # the corner offset
537 0 0       0 $tl = $fontsize ? $fontsize * 1.5 : $ty * 3;
538              
539             # Bottom aligned
540             #$ty += $linebreaks * $tl;
541             # Top aligned
542 0         0 $ty = $dy - $border - $tl;
543 0 0       0 warn 'Justified text not supported for multiline fields' if ($flags{Justify} ne 'left');
544 0         0 $tl .= ' TL';
545             }
546             else
547             {
548             #JWT: CHGD. TO NEXT (BUG#58144 PATCH): if ($flags{Justify} ne 'left' && $fontmetrics)
549 23 100       73 if ($flags{Justify} ne 'left')
550             {
551             #JWT: CHGD. TO NEXT 8: my $width = $stringwidth || $self->getStringWidth($fontmetrics, $text);
552 13         20 my $width;
553 13 50 33     95 if ($stringwidth || $fontmetrics) {
554             #JWT:CHGD TO NEXT PER BUG#122890: $width = $self->getStringWidth($fontmetrics, $text);
555 0         0 $width = $self->getStringWidth($fontmetrics, (substr $text, 1, (length $text)-2));
556             } else { #JWT: NO FONT METRICS, SO HAVE TO GUESS WIDTH:
557 13         31 $width = (length($text)-1) * 0.57; #JWT:FIXME (HACK) FOR RIGHT-JUSTIFYING STANDARD SIZE 8 NUMERIC FONT.
558 13         21 my $commas = $text;
559 13         61 $width -= 0.29 while ($commas =~ s/\,//o); #JWT:FIXME (HACK) FUDGE FOR WIDTH OF COMMAS (SMALLER THAN DIGITS)
560             }
561 13         25 my $diff = $dx - $width * $fontsize;
562 13 50       30 $diff = 0 if ($diff < 0); #JWT:ADDED.
563              
564 13 100       39 if ($flags{Justify} eq 'center')
    50          
565             {
566 1         9 $text = ($diff/2)." 0 Td $text";
567             }
568             elsif ($flags{Justify} eq 'right')
569             {
570 12         77 $text = "$diff 0 Td $text";
571             }
572             }
573             }
574              
575             # Move text from lower left corner of form field
576 23         122 my $tm = "1 0 0 1 $tx $ty Tm ";
577              
578             # if not 'none', draw a background as a filled rectangle of solid color
579             my $background_color
580             = $opts{background_color} eq 'none' ? q{}
581 23 50       89 : ref $opts{background_color} ? "@{$opts{background_color}} rgb"
  0 50       0  
582             : "$opts{background_color} g";
583 23 50       146 my $background = $background_color ? "$background_color 0 0 $dx $dy re f" : q{};
584              
585 23         74 $text = "$tl $da $tm $text Tj";
586 23         133 $text = "$background /Tx BMC q 1 1 ".($dx-$border).q{ }.($dy-$border)." re W n BT $text ET Q EMC";
587 23 50       53 unless ($fieldType eq 'Btn') #JWT:ADDED CONDITION:
588             {
589 23         83 $formdict->{Length} = CAM::PDF::Node->new('number', length($text), $formonum, $formgnum);
590             # JWT:NEXT 3 ADDED PER BUG#125299 PATCH:
591 23         384 $formdict->{StreamData} = CAM::PDF::Node->new('stream', $text, $formonum, $formgnum);
592 23         321 delete $formdict->{ Filter };
593 23         79 $self-> encodeObject( $formonum, 'FlateDecode' );
594             }
595              
596 23 50       12692 if (@rsrcs > 0) {
597             $formdict->{Resources} = CAM::PDF::Node->new('dictionary', {}, $formonum, $formgnum)
598 23 50       92 unless ($formdict->{Resources});
599              
600 23         349 my $rdict = $self->getValue($formdict->{Resources});
601 23 50       211 unless ($rdict->{ProcSet})
602             {
603 23         70 $rdict->{ProcSet} = CAM::PDF::Node->new('array',
604             [
605             CAM::PDF::Node->new('label', 'PDF', $formonum, $formgnum),
606             CAM::PDF::Node->new('label', 'Text', $formonum, $formgnum),
607             ],
608             $formonum, $formgnum);
609             }
610             $rdict->{Font} = CAM::PDF::Node->new('dictionary', {}, $formonum, $formgnum)
611 23 50       869 unless ($rdict->{Font});
612              
613 23         365 my $fdict = $self->getValue($rdict->{Font});
614              
615             # Search out font resources. This is a total kluge.
616             # TODO: the right way to do this is to look for the DR
617             # attribute in the form element or it's ancestors.
618 23         199 for my $font (@rsrcs)
619             {
620             #JWT: CHGD. TO NEXT 11 (BUG#58144 PATCH): my $fobj = $self->dereference("/$font", 'All');
621             #JWT: CHGD. TO NEXT 11 (BUG#58144 PATCH): if (!$fobj)
622 23         57 my $root = $self->getRootDict()->{AcroForm};
623 23         777 my $ifdict = $self->getValue($root);
624 23 50       633 unless (exists $ifdict->{DR})
625             {
626             #JWT:die "Could not find resource /$font while preparing form field $key\n";
627 0         0 warn "Could not find resource1 /$font while preparing form field $key\n";
628             }
629 23         53 my $dr = $self->getValue($ifdict->{DR});
630 23         238 my $fobjnum = $dr->{Font}->{value}->{$font}->{value};
631              
632 23 50       65 unless ($fobjnum)
633             {
634             #JWT:die "Could not find resource /$font while preparing form field $key\n";
635 0         0 warn "Could not find resource2 /$font while preparing form field $key\n";
636             }
637             #JWT: CHGD. TO NEXT (BUG#58144 PATCH): $fdict->{$font} = CAM::PDF::Node->new('reference', $fobj->{objnum}, $formonum, $formgnum);
638 23         56 $fdict->{$font} = CAM::PDF::Node->new('reference', $fobjnum, $formonum, $formgnum);
639             }
640             }
641             }
642 23         528 $filled++;
643             }
644              
645 12         60 return $filled;
646             }
647              
648             =item $doc->getFormFieldList()
649              
650             Return an array of the names of all of the PDF form fields. The names
651             are the full hierarchical names constructed as explained in the PDF
652             reference manual. These names are useful for the fillFormFields()
653             function.
654              
655             =cut
656              
657             sub getFormFieldList
658             {
659 222     222 1 184565 my $self = shift;
660 222         361 my $parentname = shift; # very optional
661              
662 222 100       549 my $prefix = (defined $parentname ? $parentname . q{.} : q{});
663              
664 222         292 my $kidlist;
665 222 100 66     837 if (defined $parentname && $parentname ne q{})
666             {
667 220         518 my $parent = $self->getFormField($parentname);
668 220 50       459 return unless ($parent);
669 220         481 my $dict = $self->getValue($parent);
670 220 100       3063 return unless (exists $dict->{Kids});
671 4         12 $kidlist = $self->getValue($dict->{Kids});
672             }
673             else
674             {
675 2         11 my $root = $self->getRootDict()->{AcroForm};
676 2 50       77 return unless ($root);
677 2         6 my $parent = $self->getValue($root);
678 2 50       3800 return unless (exists $parent->{Fields});
679 2         11 $kidlist = $self->getValue($parent->{Fields});
680             }
681              
682 6         52 my @list;
683 6         14 my $nonamecnt = '0';
684 6         9 for my $kid (@{$kidlist})
  6         16  
685             {
686 218 50 33     1289 if ((! ref $kid) || (ref $kid) ne 'CAM::PDF::Node' || $kid->{type} ne 'reference')
      33        
687             {
688 0         0 die "Expected a reference as the form child of '$parentname'\n";
689             }
690 218         586 my $objnode = $self->dereference($kid->{value});
691 218         299317 my $dict = $self->getValue($objnode);
692 218         3116 my $name = "(no name$nonamecnt)"; # assume the worst
693 218         389 ++$nonamecnt;
694 218 50       859 $name = $self->getValue($dict->{T}) if (exists $dict->{T});
695 218         2009 $name = $prefix . $name;
696 218         1349 $name =~ s/\x00//gso; #JWT:HANDLE IRS'S FSCKED-UP HIGH-ASCII FIELD NAMES!
697 218         465 push @list, $name;
698 218 50       485 push @list, $prefix . $self->getValue($dict->{TU}) . ' (alternate name)' if (exists $dict->{TU});
699 218         705 $self->{formcache}->{$name} = $objnode;
700 218         565 my @kidnames = $self->getFormFieldList($name);
701 218 100       680 if (@kidnames > 0)
702             {
703             #push @list, 'descend...';
704 4         67 push @list, @kidnames;
705             #push @list, 'ascend...';
706             }
707             }
708 6         231 return @list;
709             }
710              
711             =item $doc->getFormField($name)
712              
713             I
714              
715             Return the object containing the form field definition for the
716             specified field name. C<$name> can be either the full name or the
717             "short/alternate" name.
718              
719             =cut
720              
721             sub getFormField
722             {
723 248     248 1 411 my $self = shift;
724 248         382 my $fieldname = shift;
725              
726 248 50       518 return unless (defined $fieldname);
727              
728 248 50       570 unless (exists $self->{formcache}->{$fieldname})
729             {
730 0         0 my $kidlist;
731             my $parent;
732 0 0       0 if ($fieldname =~ m/ [.] /xms)
733             {
734 0         0 my $parentname;
735             #JWT: CHGD. TO NEXT (BUG#58144 PATCH): if ($fieldname =~ s/ \A(.*)[.]([.]+)\z /$2/xms)
736 0 0       0 $parentname = $1 if ($fieldname =~ s/ \A(.*)[.]([^.]+)\z /$2/xms);
737 0 0       0 return unless ($parentname);
738 0         0 $parent = $self->getFormField($parentname);
739 0 0       0 return unless ($parent);
740 0         0 my $dict = $self->getValue($parent);
741 0 0       0 return unless (exists $dict->{Kids});
742 0         0 $kidlist = $self->getValue($dict->{Kids});
743             }
744             else
745             {
746 0         0 my $root = $self->getRootDict()->{AcroForm};
747 0 0       0 return unless ($root);
748 0         0 $parent = $self->dereference($root->{value});
749 0 0       0 return unless ($parent);
750 0         0 my $dict = $self->getValue($parent);
751 0 0       0 return unless (exists $dict->{Fields});
752 0         0 $kidlist = $self->getValue($dict->{Fields});
753             }
754              
755 0         0 $self->{formcache}->{$fieldname} = undef; # assume the worst...
756 0         0 for my $kid (@{$kidlist})
  0         0  
757             {
758 0         0 my $objnode = $self->dereference($kid->{value});
759 0         0 $objnode->{formparent} = $parent;
760 0         0 my $dict = $self->getValue($objnode);
761 0 0       0 $self->{formcache}->{$self->getValue($dict->{T})} = $objnode if (exists $dict->{T});
762 0 0       0 $self->{formcache}->{$self->getValue($dict->{TU})} = $objnode if (exists $dict->{TU});
763             }
764             }
765              
766 248         510 return $self->{formcache}->{$fieldname};
767             }
768              
769             =item $doc->writeAny($node)
770              
771             Returns the serialization of the specified node. This handles all
772             Node types, including object Nodes.
773              
774             =cut
775              
776             sub writeAny
777             {
778 22413     22413 1 6036425 my $self = shift;
779 22413         29585 my $objnode = shift;
780              
781 22413 50       42117 die 'Not a ref' unless (ref $objnode);
782              
783 22413         40706 my $key = $objnode->{type};
784              
785 22413 50 33     60131 return 1 unless (defined($key) && $key); #JWT:ADDED!
786              
787 22413         35445 my $val = $objnode->{value};
788 22413         34893 my $objnum = $objnode->{objnum};
789 22413         32059 my $gennum = $objnode->{gennum};
790              
791             return $key eq 'string' ? $self->writeString($self->{crypt}->encrypt($self, $val, $objnum, $gennum))
792 22413 50 0     80990 : $key eq 'hexstring' ? '<' . (unpack 'H*', $self->{crypt}->encrypt($self, $val, $objnum, $gennum)) . '>'
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
793             : $key eq 'number' ? "$val"
794             : $key eq 'reference' ? "$val 0 R" # TODO: lookup the gennum and use it instead of 0 (?)
795             : $key eq 'boolean' ? $val
796             : $key eq 'null' ? 'null'
797             : $key eq 'label' ? "/$val"
798             : $key eq 'array' ? $self->_writeArray($objnode)
799             : $key eq 'dictionary' ? $self->_writeDictionary($objnode)
800             : $key eq 'object' ? $self->_writeObject($objnode)
801             #JWT:CHGD. TO NEXT (TO PREVENT DEATH!): : die "Unknown key '$key' in writeAny (objnum ".($objnum||'').")\n";
802             : warn "Unknown key '$key' (value=$val= objnum=$objnum gen=$gennum) in writeAny (objnum ".($objnum||'').")\n";
803             }
804              
805             1;
806              
807             __END__