File Coverage

blib/lib/CAM/PDF/Content.pm
Criterion Covered Total %
statement 168 202 83.1
branch 50 84 59.5
condition 19 29 65.5
subroutine 20 20 100.0
pod 8 8 100.0
total 265 343 77.2


line stmt bran cond sub pod time code
1             package CAM::PDF::Content;
2              
3 1     1   29 use 5.006;
  1         3  
  1         49  
4 1     1   6 use warnings;
  1         1  
  1         44  
5 1     1   5 use strict;
  1         1  
  1         31  
6 1     1   6 use Carp;
  1         1  
  1         77  
7 1     1   5 use English qw(-no_match_vars);
  1         2  
  1         7  
8 1     1   530 use CAM::PDF;
  1         2  
  1         19  
9 1     1   6 use CAM::PDF::Node;
  1         2  
  1         4444  
10              
11             our $VERSION = '1.60';
12              
13             =for stopwords renderers unvalidated
14              
15             =head1 NAME
16              
17             CAM::PDF::Content - PDF page layout parser
18              
19             =head1 LICENSE
20              
21             Same as L
22              
23             =head1 SYNOPSIS
24              
25             use CAM::PDF;
26             my $pdf = CAM::PDF->new($filename);
27            
28             my $contentTree = $pdf->getPageContentTree(4);
29             $contentTree->validate() || die 'Syntax error';
30             print $contentTree->render('CAM::PDF::Renderer::Text');
31             $pdf->setPageContent(5, $contentTree->toString());
32              
33             =head1 DESCRIPTION
34              
35             This class is used to manipulate the layout commands for a single page
36             of PDF. The page content is passed as a scalar and parsed according
37             to Adobe's PDF Reference 3rd edition (for PDF v1.4). All of the
38             commands from Appendix A of that document are parsed and understood.
39              
40             Much of the content object's functionality is wrapped up in renderers
41             that can be applied to it. See the canonical renderer, CAM::PDF::GS,
42             and the render() method below for more details.
43              
44             =cut
45              
46             # Package globals:
47              
48             my %loaded; # keep track of eval'd renderers
49             my %endings = (
50             q => 'Q',
51             BT => 'ET',
52             BDC => 'EMC',
53             BMC => 'EMC',
54             BX => 'EX',
55             );
56             my $starts = join q{|}, map {quotemeta} keys %endings;
57             my $ends = join q{|}, map {quotemeta} values %endings;
58              
59             sub _buildOpSyntax
60             {
61 379     379   24296 %CAM::PDF::Content::ops = (
62             b => [],
63             B => [],
64             'b*' => [],
65             'B*' => [],
66             BDC => ['label','dictionary|label'],
67             BI => ['image'],
68             BMC => ['label'],
69             BT => [],
70             BX => [],
71             c => ['number','number','number','number','number','number'],
72             cm => ['number','number','number','number','number','number'],
73             CS => ['label'],
74             cs => ['label'],
75             d => ['array','number'],
76             d0 => ['number','number'],
77             d1 => ['number','number','number','number','number','number'],
78             Do => ['label'],
79             DP => ['label','dictionary'],
80             EI => ['end'],
81             EMC => ['end'],
82             ET => ['end'],
83             EX => ['end'],
84             F => [],
85             f => [],
86             'f*' => [],
87             G => ['number'],
88             g => ['number'],
89             gs => ['label'],
90             h => [],
91             i => ['number'],
92             ID => ['end'],
93             j => ['integer'],
94             J => ['integer'],
95             K => ['number','number','number','number'],
96             k => ['number','number','number','number'],
97             l => ['number','number'],
98             m => ['number','number'],
99             M => ['number'],
100             MP => ['label'],
101             n => [],
102             q => [],
103             Q => ['end'],
104             re => ['number','number','number','number'],
105             RG => ['number','number','number'],
106             rg => ['number','number','number'],
107             ri => ['...'], # not really variable, I just don't understand this one
108             s => [],
109             S => [],
110             SC => ['...'],
111             sc => ['...'],
112             SCN => ['...'],
113             scn => ['...'],
114             sh => ['label'],
115             'T*' => [],
116             Tc => ['number'],
117             TD => ['number','number'],
118             Td => ['number','number'],
119             Tf => ['label','number'],
120             TJ => ['array'],
121             Tj => ['string'],
122             TL => ['number'],
123             Tm => ['number','number','number','number','number','number'],
124             Tr => ['integer'],
125             Ts => ['number'],
126             Tw => ['number'],
127             Tz => ['number'],
128             v => ['number','number','number','number'],
129             w => ['number'],
130             W => [],
131             'W*' => [],
132             y => ['number','number','number','number'],
133             q{'} => ['string'],
134             q{"} => ['number','number','string'],
135             );
136 379         1682 return;
137             }
138              
139             =head1 FUNCTIONS
140              
141             =over
142              
143             =item $pkg->new($content)
144              
145             =item $pkg->new($content, $data)
146              
147             =item $pkg->new($content, $data, $verbose)
148              
149             Parse a scalar CONTENT containing PDF page layout content. Returns a parsed,
150             but unvalidated, data structure.
151              
152             The DATA argument is a hash reference of contextual data that may be
153             needed to work with content. This is only needed for toString()
154             method (which needs C CAM::PDF object> to work with images)
155             and the render methods, to which the DATA reference is passed
156             verbatim. See the individual renderer modules for details about
157             required elements.
158              
159             The VERBOSE boolean indicates whether the parser should Carp when it
160             encounters problems. The default is false.
161              
162             =cut
163              
164             sub new
165             {
166 13     13 1 29 my $pkg = shift;
167 13         30 my $content = shift;
168 13         21 my $refs = shift;
169 13         19 my $verbose = shift;
170              
171 13   100     127 my $self = bless {
172             refs => $refs || {},
173             content => $content,
174             blocks => [],
175             verbose => $verbose,
176             }, $pkg;
177 13         54 return $self->parse(\$content);
178             }
179              
180             =item $self->parse($contentref)
181              
182             This is intended to be called by the new() method. The argument
183             should be a reference to the content scalar. It's passed by reference
184             so it is never copied.
185              
186             =cut
187              
188             my $progress = 0;
189             sub parse
190             {
191 13     13 1 24 my $self = shift;
192 13         20 my $c = shift;
193              
194 13         30 $progress = 0;
195 13         23 pos(${$c}) = 0; ## no critic(CodeLayout::ProhibitParensWithBuiltins)
  13         62  
196 13         28 ${$c} =~ m/ \A \s+ /cgxms; # prime the regex
  13         269  
197 13         90 my $result = $self->_parseBlocks($c, $self->{blocks});
198 13 50       76 if (!defined $result)
199             {
200 0 0       0 if ($self->{verbose})
201             {
202 0         0 carp 'Parse failed';
203             }
204 0         0 return;
205             }
206 13 50       17 if (${$c} =~ m/ \G\S /cgxms)
  13         44  
207             {
208 0 0       0 if ($self->{verbose})
209             {
210 0         0 carp 'Trailing unparsed content: ' . CAM::PDF->trimstr(${$c});
  0         0  
211             }
212 0         0 return;
213             }
214 13         67 return $self;
215             }
216              
217             # Internal method
218             #
219              
220             sub _parseBlocks
221             {
222 769     769   977 my $self = shift;
223 769         980 my $c = shift;
224 769         942 my $A_blocks = shift;
225 769         1130 my $end = shift;
226              
227 769         943 my @stack;
228 769         812 while (${$c} =~ m/ \G.*\S /xms)
  23955         97213  
229             {
230 23942         62263 my $block = $self->_parseBlock($c, $end);
231 23942 50       54228 if (!defined $block)
232             {
233 0         0 return;
234             }
235 23942 100       45752 if (!$block)
236             {
237 756         2890 return $self;
238             }
239 23186 100 100     101407 if ($block->{type} eq 'block' || $block->{type} eq 'op')
240             {
241 6362         6797 push @{$block->{args}}, @stack;
  6362         15708  
242 6362         10886 @stack = ();
243 6362         6508 push @{$A_blocks}, $block;
  6362         13766  
244             }
245             else
246             {
247 16824         29095 push @stack, $block;
248             }
249             }
250 13 50       73 if (@stack > 0)
251             {
252 0 0       0 if ($self->{verbose})
253             {
254 0         0 carp 'Error: '.@stack.' unprocessed arguments';
255             }
256 0         0 return;
257             }
258 13         39 return $self;
259             }
260              
261             # Internal method
262             #
263              
264             sub _parseBlock
265             {
266 23942     23942   27860 my $self = shift;
267 23942         24214 my $c = shift;
268 23942         26927 my $end = shift;
269              
270             # Start a new block?
271 23942 100       27154 if (${$c} =~ m/ \G($starts)\s* /ocgxms)
  23942         72079  
272             {
273 756         1454 my $type = $1;
274 756         1359 my $blocks = [];
275 756 50       2512 if ($self->_parseBlocks($c, $blocks, $endings{$type}))
276             {
277 756         2331 return _b('block', $type, $blocks);
278             }
279             else
280             {
281 0         0 return;
282             }
283             }
284              
285             # Balanced end to open block?
286 23186 100 100     52208 if (defined $end && ${$c} =~ m/ \G$end\s* /cgxms)
  23165         120584  
287             {
288 756         1908 return q{};
289             }
290              
291             # Unbalanced end?
292 22430 50       26037 if (${$c} =~ m/ \G($ends)\s* /ocgxms)
  22430         68523  
293             {
294 0         0 my $op = $1;
295 0 0       0 if ($self->{verbose})
296             {
297 0 0       0 if ($end)
298             {
299 0         0 carp "Wrong block ending (expected '$end', got '$op')";
300             }
301             else
302             {
303 0         0 carp "Unexpected block ending '$op'";
304             }
305             }
306 0         0 return;
307             }
308              
309             # Inline image?
310 22430 100       25563 if (${$c} =~ m/ \G BI \b /xms)
  22430         52520  
311             {
312 3         15 my $img = CAM::PDF->parseInlineImage($c);
313 3 50       10 if (!$img)
314             {
315 0 0       0 if ($self->{verbose})
316             {
317 0         0 carp 'Failed to parse inline image';
318             }
319 0         0 return;
320             }
321 3         11 my $blockimage = _b('op', 'BI', _b('image', $img->{value}));
322 3         12 return $blockimage;
323             }
324              
325             # Non-block operand?
326              
327             =for referencecode
328             ## This is the REAL list
329             #if (${$c} =~ m/ \G(
330             # [bBcdfFgGhijJkKlmMnsSvwWy\'\"]|
331             # b\*|B\*|BDC|BI|d[01]|c[sm]|CS|Do|DP|f\*|gs|MP|
332             # re|RG|rg|ri|sc|SC|scn|SCN|sh|T[cdDfJjLmrswz\*]|W\*
333             # )\b\s*
334             # /cgxms)
335              
336             =cut
337              
338             ## This is a cheat version of the above
339 22427 100       25216 if (${$c} =~ m/ \G([A-Za-z\'\"][*\w]*)\s* /cgxms) ## no critic (ProhibitEnumeratedClasses,ProhibitEscapedMetacharacters)
  22427         65519  
340             {
341 5603         10296 my $op = $1;
342 5603         10686 return _b('op', $op);
343             }
344              
345             # If we get here, it's data instead of an operand
346              
347 16824         52923 my $node = CAM::PDF->parseAny($c);
348 16824         48803 return _b($node->{type}, $node->{value});
349             }
350              
351             =item $self->validate()
352              
353             Returns a boolean if the parsed content tree conforms to the PDF
354             specification.
355              
356             =cut
357              
358             sub validate ## no critic(Subroutines::ProhibitExcessComplexity)
359             {
360 379     379 1 5598 my $self = shift;
361 379   66     935 my $blocks = shift || $self->{blocks};
362              
363 379         1002 $self->_buildOpSyntax();
364              
365 379         814 BLOCK:
366 379         422 foreach my $block (@{$blocks})
367             {
368 3162 100       11635 if ($block->{type} eq 'block')
    50          
369             {
370 373 50       991 return if (!$self->validate($block->{value}));
371             }
372             elsif ($block->{type} ne 'op')
373             {
374 0 0       0 if ($self->{verbose})
375             {
376 0         0 carp 'Neither a block nor an op';
377             }
378 0         0 return;
379             }
380              
381 3162         6397 my $syntax = $CAM::PDF::Content::ops{$block->{name}};
382 3162 50       6524 if ($syntax)
383             {
384 3162 100 100     12347 if ($syntax->[0] && $syntax->[0] eq '...')
    50          
385 3073         6399 {
386             # variable args, skip
387 89         397 next BLOCK;
388             }
389 3073         7690 elsif (@{$block->{args}} != @{$syntax})
390             {
391 0 0       0 if ($self->{verbose})
392             {
393 0         0 carp "Wrong number of arguments to '$block->{name}' (got ".@{$block->{args}}.' instead of '.@{$syntax}.')';
  0         0  
  0         0  
394             }
395 0         0 return;
396             }
397              
398             ARG:
399 3073         3648 foreach my $i (0 .. $#{$syntax})
  3073         6696  
400             {
401 8246         14401 my $arg = $block->{args}->[$i];
402 8246         11757 my $types = $syntax->[$i];
403              
404             ARGTYPE_OPT:
405 8246         14710 foreach my $type (split /[|]/xms, $types)
406             {
407             # These are the successful match cases
408 8246 100       38471 next ARG if ($arg->{type} eq $type);
409 4 50 33     54 next ARG if ($type eq 'integer' && $arg->{type} eq 'number' && $arg->{value} =~ m/ \A\d+\z /xms);
      33        
410 0 0 0     0 next ARG if ($type eq 'string' && $arg->{type} eq 'hexstring');
411             }
412              
413 0 0       0 if ($self->{verbose})
414             {
415 0         0 carp "Expected '$types' argument for '$block->{name}' (got $arg->{type})";
416             }
417 0         0 return;
418             }
419             }
420             }
421 379         1395 return $self;
422             }
423              
424             =item $self->render($rendererclass)
425              
426             Traverse the content tree using the specified rendering class. See
427             CAM::PDF::GS or CAM::PDF::Renderer::Text for renderer examples.
428             Renderers should typically derive from CAM::PDF::GS, but it's not
429             essential. Typically returns an instance of the renderer class.
430              
431             The rendering class is loaded via C if not already in memory.
432              
433             =cut
434              
435             sub render
436             {
437 11     11 1 31 my $self = shift;
438 11         23 my $renderer = shift; # a package name
439              
440 11 100       70 if (!$loaded{$renderer})
441             {
442 2 50       280 if (!eval "require $renderer") ## no critic (StringyEval)
443             {
444 0         0 die $EVAL_ERROR;
445             }
446 2         13 $loaded{$renderer} = 1;
447             }
448 11         54 return $self->traverse($renderer);
449             }
450              
451             =item $self->computeGS()
452              
453             =item $self->computeGS($skiptext)
454              
455             Traverses the content tree and computes the coordinates of each
456             graphic point along the way. If the C<$skiptext> boolean is true
457             (default: false) then text blocks are ignored to save time, since they
458             do not change the global graphic state.
459              
460             This is a thin wrapper around render() with CAM::PDF::GS or
461             CAM::PDF::GS::NoText selected as the rendering class.
462              
463             =cut
464              
465             sub computeGS
466             {
467 6     6 1 16136 my $self = shift;
468 6         16 my $skip_text = shift;
469              
470 6 50       43 return $self->render('CAM::PDF::GS' . ($skip_text ? '::NoText' : q{}));
471             }
472              
473             =item $self->findImages()
474              
475             Traverse the content tree, accumulating embedded images and image
476             references, according to the CAM::PDF::Renderer::Images renderer.
477              
478             =cut
479              
480             sub findImages
481             {
482 5     5 1 166461 my $self = shift;
483              
484 5         37 return $self->render('CAM::PDF::Renderer::Images');
485             }
486              
487             =item $self->traverse($rendererclass)
488              
489             This recursive method is typically called only by wrapper methods,
490             like render(). It instantiates renderers as needed and calls methods
491             on them.
492              
493             =cut
494              
495             sub traverse
496             {
497 756     756 1 1076 my $self = shift;
498 756         926 my $renderer = shift; # class
499 756   66     1983 my $blocks = shift || $self->{blocks};
500 756         872 my $gs = shift;
501              
502 756 100       1620 if (!$gs)
503             {
504 11         120 $gs = $renderer->new($self->{refs});
505             }
506              
507 756         1067 foreach my $block (@{$blocks})
  756         1493  
508             {
509 6322         12886 $block->{gs} = $gs;
510              
511             # Enact the GS change performed by this operation
512 6322         15220 my $func = $block->{name};
513 6322         7801 $func =~ s/ [*] /star/gxms;
514 6322         6514 $func =~ s/ \' /quote/gxms;
515 6322         6678 $func =~ s/ \" /doublequote/gxms;
516              
517 6322 100       23900 if ($gs->can($func))
518             {
519 2655         7772 my $newgs = $gs->clone();
520              
521             {
522 1     1   11 no strict 'refs'; ## no critic(ProhibitNoStrict)
  1         2  
  1         456  
  2655         3898  
523 2655         3277 $newgs->$func(map {$_->{value}} @{$block->{args}});
  8228         37883  
  2655         7866  
524             }
525              
526 2655         6132 $gs = $newgs;
527             }
528              
529 6322 100       19558 if ($block->{type} eq 'block')
530             {
531 745         2401 my $newgs = $self->traverse($renderer, $block->{value}, $gs);
532 745 100       3680 if ($block->{name} ne 'q')
533             {
534 328         873 $gs = $newgs;
535             }
536             }
537             }
538 756         1856 return $gs;
539             }
540              
541             =item $self->toString()
542              
543             Flattens a content tree back into a scalar, ready to be inserted back
544             into a PDF document. Since whitespace is discarded by the parser, the
545             resulting scalar will not be identical to the original.
546              
547             =cut
548              
549             sub toString
550             {
551 11     11 1 17 my $self = shift;
552 11   66     37 my $blocks = shift || $self->{blocks};
553              
554 11         16 my $str = q{};
555 11         22 my $doc = $self->{refs}->{doc};
556 11         13 foreach my $block (@{$blocks})
  11         26  
557             {
558 38 100       97 if ($block->{name} eq 'BI')
559             {
560 1         7 $str .= $doc->writeInlineImage($block->{args}->[0]) . "\n";
561             }
562             else
563             {
564 37         191 foreach my $arg (@{$block->{args}})
  37         83  
565             {
566 76         728 $str .= $doc->writeAny($arg) . q{ };
567             }
568 37         70 $str .= $block->{name} . "\n";
569 37 100       298 if ($block->{type} eq 'block')
570             {
571 10         37 $str .= $self->toString($block->{value});
572 10         33 $str .= $endings{$block->{name}} . "\n";
573             }
574             }
575             }
576 11         36 return $str;
577             }
578              
579             # internal function
580             # Node creator
581              
582             sub _b
583             {
584 23189     23189   57812 my ($type, @args) = @_;
585 23189 100       52449 if ($type eq 'block')
    100          
586             {
587             return {
588 756         5074 type => $type,
589             name => shift @args,
590             value => shift @args,
591             args => \@args,
592             };
593             }
594             elsif ($type eq 'op')
595             {
596             return {
597 5606         43825 type => $type,
598             name => shift @args,
599             args => \@args,
600             };
601             }
602             else
603             {
604             return {
605 16827         121356 type => $type,
606             value => shift @args,
607             args => \@args,
608             };
609             }
610             }
611              
612             1;
613             __END__