File Coverage

blib/lib/PDF/Builder/Page.pm
Criterion Covered Total %
statement 196 300 65.3
branch 69 150 46.0
condition 31 76 40.7
subroutine 30 37 81.0
pod 17 24 70.8
total 343 587 58.4


line stmt bran cond sub pod time code
1             package PDF::Builder::Page;
2              
3 39     39   407 use base 'PDF::Builder::Basic::PDF::Pages';
  39         102  
  39         6461  
4              
5 39     39   293 use strict;
  39         99  
  39         1179  
6 39     39   206 use warnings;
  39         92  
  39         3991  
7              
8             our $VERSION = '3.028'; # VERSION
9             our $LAST_UPDATE = '3.028'; # manually update whenever code is changed
10              
11 39     39   323 use Carp;
  39         122  
  39         3587  
12 39     39   279 use POSIX qw(floor);
  39         143  
  39         403  
13 39     39   3761 use Scalar::Util qw(looks_like_number weaken);
  39         160  
  39         2556  
14              
15 39     39   278 use PDF::Builder::Basic::PDF::Utils;
  39         131  
  39         4345  
16 39     39   45631 use PDF::Builder::Content;
  39         222  
  39         2218  
17 39     39   581 use PDF::Builder::Content::Text;
  39         125  
  39         1168  
18 39     39   231 use PDF::Builder::Util;
  39         109  
  39         194464  
19              
20             =head1 NAME
21              
22             PDF::Builder::Page - Methods to interact with individual pages
23              
24             Inherits from L<PDF::Builder::Basic::PDF::Pages>
25              
26             =head1 SYNOPSIS
27              
28             my $pdf = PDF::Builder->new();
29              
30             # Add a page to a new or existing PDF
31             my $page = $pdf->page();
32              
33             # Set the physical (media) page size
34             # Set prepress page boundaries, a convenience function for those times when
35             # it is not necessary to set other prepress (print-related) page boundaries
36             $page->size('letter'); # by common size name
37             #$page->size([0, 0, 612, 792]); # by points LLx,LLy, URx,URy
38              
39             # alternately, can set (or get) prepress page boundaries
40             $page->boundaries('media' => '12x18', 'trim' => 0.5 * 72);
41              
42             # Add an image
43             my $image = $pdf->image('/path/to/file.jpg');
44             $page->object($image, $x,$y, $w,$h);
45              
46             # Create a content object for text
47             my $text = $page->text();
48              
49             # Create a content object for drawing shapes
50             my $canvas = $page->graphics(); # or gfx()
51              
52             # Now to draw graphics (using $canvas object) and text (using $text object).
53             # NOTE that everything in the graphics (canvas) object will be laid down on
54             # the page BEFORE anything in the text object is laid down. That is,
55             # text will cover graphics, but not vice-versa. This is simply due to
56             # the order in which the objects were defined.
57            
58             =head1 METHODS
59              
60             =head2 new
61              
62             $page = PDF::Builder::Page->new($pdf, $parent, $index)
63              
64             =over
65              
66             Returns a page object (called from $pdf->page()).
67              
68             =back
69              
70             =cut
71              
72             sub new {
73 192     192 1 690 my ($class, $pdf, $parent, $index) = @_;
74 192         478 my $self = {};
75              
76 192 50       596 $class = ref($class) if ref($class);
77 192         1116 $self = $class->SUPER::new($pdf, $parent);
78 192         785 $self->{'Type'} = PDFName('Page');
79 192         1164 $self->proc_set(qw( PDF Text ImageB ImageC ImageI ));
80 192         900 delete $self->{'Count'};
81 192         737 delete $self->{'Kids'};
82 192         1109 $parent->add_page($self, $index);
83              
84             # copy global UU (if exists) to local, possibly to be overridden
85             # we can access UserUnit (should be not 1.0, if exists) but not userUnit
86 192 50       809 if (defined $self->{'Parent'}->{'UserUnit'}) {
87 0         0 my $UU = $self->{'Parent'}->{'UserUnit'}->{'val'};
88 0         0 $self->{' userUnit'} = $UU;
89             # AND set the local one if global is not 1.0
90             # (some readers don't let a page inherit the global UU)
91 0 0       0 if ($UU != 1.0) {
92 0         0 $self->userunit($UU);
93             }
94             } else {
95             # not setting a global userUnit, so default this one to 1.0
96 192         688 $self->{' userUnit'} = 1;
97             # don't set a local UserUnit for now
98             }
99              
100 192         764 return $self;
101             }
102              
103             #=head2 coerce
104             #
105             # $page = PDF::Builder::Page->coerce($pdf, $pdfpage)
106             #
107             #=over
108             #
109             #Returns a page object converted from $pdfpage (called from $pdf->open_page()).
110             #
111             #=back
112             #
113             #=cut
114              
115             # appears to be unused TBD
116              
117             sub coerce {
118 0     0 0 0 my ($class, $pdf, $page) = @_;
119 0         0 my $self = $page;
120 0         0 bless $self, $class;
121 0         0 $self->{' apipdf'} = $pdf;
122 0         0 weaken $self->{' apipdf'};
123 0         0 return $self;
124             }
125              
126             #=head2 update
127             #
128             # $page->update()
129             #
130             #=over
131             #
132             #Marks a page to be updated (by $pdf->update()).
133             #
134             #=back
135             #
136             #=cut
137              
138             # appears to be internal routine
139             # replace any use by call to out_obj: $self->{' apipdf'}->out_obj($self);
140              
141             # PDF::API2 2.042: DEPRECATED
142             # Marking the page as dirty should only be needed in rare cases
143             # when the page hash is being edited directly rather than through the API. In
144             # that case, the out_obj call can be made manually. There's no reason (that I
145             # can think of) to have a specific call just (and only) for Page objects.
146              
147             sub update {
148 1     1 0 11 my $self = shift;
149              
150 1         5 $self->{' apipdf'}->out_obj($self);
151 1         2 return $self;
152             }
153              
154             =head2 Page Size Methods
155              
156             =head3 Page Sizes
157              
158             PDF page sizes are stored as rectangular coordinates. For convenience,
159             PDF::Builder also supports a number of aliases and shortcuts that are more
160             human-friendly. The following formats are available:
161              
162             =over
163              
164             =item a standard paper size
165              
166             $page->boundaries('media' => 'A4');
167              
168             Aliases for the most common paper sizes are built in (case-insensitive).
169             US: Letter, Legal, Ledger, Tabloid (and others)
170             Metric: 4A0, 2A0, A0 - A6, 4B0, 2B0, and B0 - B6 (and others)
171              
172             See L<PDF::Builder::Resource::PaperSizes> for the full list.
173              
174             =item a "WxH" string in inches
175              
176             $page->boundaries('media' => '8.5x11');
177              
178             Many US paper sizes are commonly identified by their size in inches rather than
179             by a particular name. These can be passed as strings with the width and height
180             separated by an C<x>.
181             Examples: C<4x6>, C<12x18>, C<8.5x11>
182              
183             =item a number representing a reduction (in points) from the next-larger box
184              
185             For example, a 12" x 18" physical sheet to be trimmed down to an 11" x 17" sheet
186             can be specified as follows:
187              
188             # Note: There are 72 points per inch
189             $page->boundaries('media' => '12x18', 'trim' => 0.5 * 72);
190              
191             # Equivalent
192             $page->boundaries('media' => [0, 0, 12 * 72, 18 * 72],
193             'trim' => [0.5 * 72, 0.5 * 72, 11.5 * 72, 17.5 * 72]);
194              
195             This example shows a 12" x 18" physical sheet that will be reduced to a final
196             size of 11" x 17" by trimming 0.5" from each edge. The smaller page boundary is
197             assumed to be centered within the larger one.
198              
199             The "next-larger box" follows this order, stopping at the first defined value:
200              
201             art -> trim -> bleed -> media
202             crop -> media
203              
204             This option isn't available for the media box, since it is by definition, the
205             largest boundary.
206              
207             =item [$width, $height] in points
208              
209             $page->boundaries('media' => [8.5 * 72, 11 * 7.2]);
210              
211             For other page or boundary sizes, the width and height (in points) can be given
212             directly as an array.
213              
214             =item [$x1, $y1, $x2, $y2] in points
215              
216             $page->boundaries('media' => [0, 0, 8.5 * 72, 11 * 72]);
217              
218             Finally, the absolute (raw) coordinates of the bottom-left and top-right corners
219             of a rectangle can be specified.
220              
221             =back
222              
223             =cut
224              
225             # returns ARRAY of 4 elements
226             sub _to_rectangle {
227 5     5   12 my $value = shift();
228              
229             # An array of two or four numbers in points
230 5 50       20 if (ref($value) eq 'ARRAY') {
231 0 0       0 if (@$value == 2) {
    0          
232 0         0 return (0, 0, @$value);
233             } elsif (@$value == 4) {
234 0         0 return (@$value);
235             }
236 0         0 croak "Page boundary array must contain two or four numbers";
237             }
238              
239             # WxH in inches
240 5 100       41 if ($value =~ /^([0-9.]+)\s*x\s*([0-9.]+)$/) {
241 2         15 my ($w, $h) = ($1, $2);
242 2 50 33     25 if (looks_like_number($w) and looks_like_number($h)) {
243 2         14 return (0, 0, $w * 72, $h * 72);
244             }
245             }
246              
247             # Common names for page sizes
248 3         20 my %page_sizes = PDF::Builder::Resource::PaperSizes::get_paper_sizes();
249              
250 3 50       41 if ($page_sizes{lc $value}) {
251 3         7 return (0, 0, @{$page_sizes{lc $value}});
  3         86  
252             }
253              
254 0 0       0 if (ref($value)) {
255 0         0 croak "Unrecognized page size";
256             } else {
257 0         0 croak "Unrecognized page size: $value";
258             }
259             }
260              
261             =head3 Page Boundaries
262              
263             PDF defines five page boundaries. When creating PDFs for print shops, you'll
264             most commonly use just the media box and trim box. Traditional print shops may
265             also use the bleed box when adding printer's marks and other information.
266              
267             =over
268              
269             =item media
270              
271             The media box defines the boundaries of the physical medium on which the page is
272             to be printed. It may include any extended area surrounding the finished page
273             for bleed, printing marks, or other such purposes. The default value is as
274             defined for PDF, a US letter page (8.5" x 11").
275             B<CAUTION:> Most printers can I<not> print all the way to the edge of the
276             physical medium (paper, etc.). Some space around the edges will be reserved
277             for pinch rollers, etc. to move the paper without smearing around toner or ink.
278              
279             =item crop
280              
281             The crop box defines the region to which the contents of the page shall be
282             clipped (cropped) when displayed or printed. The default value is the page's
283             media box.
284             This is a historical page boundary. You'll likely want to set the bleed and/or
285             trim boxes instead.
286              
287             =item bleed
288              
289             The bleed box defines the region to which the contents of the page shall be
290             clipped when output in a production environment. This may include any extra
291             bleed area needed to accommodate the physical limitations of cutting, folding,
292             and trimming equipment. The actual printed page (media box) may include
293             printing marks that fall outside the bleed box. The default value is the page's
294             crop box.
295              
296             =item trim
297              
298             The trim box defines the intended dimensions of the finished page after
299             trimming. It may be smaller than the media box to allow for production-related
300             content, such as printing instructions, cut marks, or color bars. The default
301             value is the page's crop box.
302              
303             =item art
304              
305             The art box defines the extent of the page's meaningful content (including
306             potential white space) as intended by the page's creator. The default value is
307             the page's crop box.
308              
309             =back
310              
311             =head3 userunit
312              
313             $page->userunit($value)
314              
315             =over
316              
317             Sets the User Unit for this one page.
318             See L<PDF::Builder::Docs/User Units> for more information.
319              
320             =back
321              
322             =cut
323              
324             sub userunit {
325 0     0 1 0 my ($self, $value) = @_;
326              
327 0 0       0 if (float($value) <= 0.0) {
328 0         0 warn "Invalid User Unit value '$value', set to 1.0";
329 0         0 $value = 1.0;
330             }
331              
332             # assume that even if $value is 1.0, it's being called for some reason
333             # (perhaps overriding non-1.0 global setting)
334 0         0 $PDF::Builder::global_pdf->verCheckOutput(1.6, "set User Unit");
335 0         0 $self->{' userUnit'} = float($value); # this is local (page) UU
336 0         0 $self->{'UserUnit'} = PDFNum(float($value));
337              
338 0         0 return $self;
339             }
340              
341             sub _bbox {
342 107     107   343 my ($box, $self, @corners) = @_;
343             # $box is the box name (e.g., MediaBox)
344             # $self points to the page object
345             # at least one element in @corners if setting. get if no elements.
346              
347             # if 1 or 3 elements in @corners, and [0] contains a letter, it's a name
348 107         179 my $isName = 0;
349 107 100 100     670 if (scalar @corners && $corners[0] =~ m/[a-z]/i) { $isName = 1; }
  29         52  
350              
351 107 100       323 if (scalar @corners == 0) {
    50          
352             # is a query ('get')
353             # if previously set for this page, just return array
354             # if not set, check parent (PDF), then up chain as far as MediaBox
355 58 100       192 if (defined $self->{$box}) {
356 43         143 return map { $_->val() } $self->{$box}->elements();
  172         378  
357             } else {
358 15         34 my $pdf = $self->{' api'};
359 15 100       52 if (defined $pdf->{'pages'}->{$box}) {
360             # parent (global) box is defined
361 11         46 return map { $_->val() } $pdf->{'pages'}->{$box}->elements();
  44         100  
362             } else {
363             # go up the chain until find defined global
364 4 100 100     39 if ($box eq 'ArtBox' || $box eq 'TrimBox' || $box eq 'BleedBox') {
      100        
365 3         9 $box = 'CropBox';
366             }
367 4 50 33     30 if ($box eq 'CropBox' && !defined $pdf->{'pages'}->{'CropBox'}) {
368 4         9 $box = 'MediaBox';
369             }
370 4 50 33     25 if ($box ne 'CropBox' && $box ne 'MediaBox') {
371             # invalid box name. silent error: just return Media Box
372 0         0 $box = 'MediaBox';
373             }
374 4         24 return map { $_->val() } $pdf->{'pages'}->{$box}->elements();
  16         42  
375             }
376             }
377            
378             } elsif (scalar @corners == 3) {
379             # have a name and one option (orient)
380 0         0 my ($name, %opts) = @corners;
381             # copy dashed option names to preferred undashed names
382 0 0 0     0 if (defined $opts{'-orient'} && !defined $opts{'orient'}) { $opts{'orient'} = delete($opts{'-orient'}); }
  0         0  
383              
384 0         0 @corners = page_size(($name)); # now 4 numeric values
385 0 0       0 if (defined $opts{'orient'}) {
386 0 0       0 if ($opts{'orient'} =~ m/^l/i) { # 'landscape' or just 'l'
387             # 0 0 W H -> 0 0 H W
388 0         0 my $temp;
389 0         0 $temp = $corners[2]; $corners[2] = $corners[3]; $corners[3] = $temp;
  0         0  
  0         0  
390             }
391             }
392             } else {
393             # name without [orient] option, or numeric coordinates given
394 49         201 @corners = page_size(@corners);
395             }
396              
397             # scale down size if User Unit given (e.g., Letter => 0 0 8.5 11)
398             # we have a global userUnit, and possibly a page userUnit overriding it
399 49 100       144 if ($isName) {
400 29         51 my $UU = $self->{' userUnit'};
401 29 50       297 if ($UU != 1.0) {
402 0         0 for (my $i=0; $i<4; $i++) {
403 0         0 $corners[$i] /= $UU;
404             }
405             }
406             }
407              
408 49         121 $self->{$box} = PDFArray( map { PDFNum(float($_)) } @corners );
  196         459  
409             # return 4 element array of box corners
410 49         175 return @corners;
411             }
412              
413             =head3 mediabox
414              
415             $page->mediabox($alias)
416              
417             $page->mediabox($alias, 'orient' => 'orientation')
418              
419             $page->mediabox($w,$h)
420              
421             $page->mediabox($llx,$lly, $urx,$ury)
422              
423             ($llx,$lly, $urx,$ury) = $page->mediabox()
424              
425             =over
426              
427             Sets or gets the Media Box for this one page.
428             See L<PDF::Builder::Docs/Media Box> for more information.
429             The method always returns the current bounds (after any set operation).
430              
431             =back
432              
433             =cut
434              
435             sub mediabox {
436 71     71 1 114717 return _bbox('MediaBox', @_);
437             }
438              
439             #=head4 get_mediabox
440             #
441             # ($llx,$lly, $urx,$ury) = $page->get_mediabox()
442             #
443             #=over
444             #
445             #Gets the Media Box corner coordinates based on best estimates or the default.
446             #These are in the order given in a mediabox call (4 coordinates).
447             #
448             #This method is B<Deprecated>, and has been B<removed>. Use
449             #the global (C<$pdf>) or page (C<$page>) mediabox() call with no parameters
450             #instead.
451             #
452             #=back
453             #
454             #=cut
455             #
456             #sub get_mediabox {
457             # my $self = shift();
458             # return $self->mediabox();
459             #}
460              
461             =head3 cropbox
462              
463             $page->cropbox($alias)
464              
465             $page->cropbox($alias, 'orient' => 'orientation')
466              
467             $page->cropbox($w,$h)
468              
469             $page->cropbox($llx,$lly, $urx,$ury)
470              
471             ($llx,$lly, $urx,$ury) = $page->cropbox()
472              
473             =over
474              
475             Sets or gets the Crop Box for this one page.
476             See L<PDF::Builder::Docs/Crop Box> for more information.
477             The method always returns the current bounds (after any set operation).
478              
479             =back
480              
481             =cut
482              
483             sub cropbox {
484 9     9 1 6917 return _bbox('CropBox', @_);
485             }
486              
487             #=head4 get_cropbox
488             #
489             # ($llx,$lly, $urx,$ury) = $page->get_cropbox()
490             #
491             #=over
492             #
493             #Gets the Crop Box based on best estimates or the default.
494             #
495             #This method is B<Deprecated>, and has been B<removed>. Use
496             #the global (C<$pdf>) or page (C<$page>) cropbox() call with no parameters
497             #instead.
498             #
499             #=back
500             #
501             #=cut
502             #
503             #sub get_cropbox {
504             # my $self = shift();
505             # return $self->cropbox();
506             #}
507              
508             =head3 bleedbox
509              
510             $page->bleedbox($alias)
511              
512             $page->bleedbox($alias, 'orient' => 'orientation')
513              
514             $page->bleedbox($w,$h)
515              
516             $page->bleedbox($llx,$lly, $urx,$ury)
517              
518             ($llx,$lly, $urx,$ury) = $page->bleedbox()
519              
520             =over
521              
522             Sets or gets or gets the Bleed Box for this one page.
523             See L<PDF::Builder::Docs/Bleed Box> for more information.
524             The method always returns the current bounds (after any set operation).
525              
526             =back
527              
528             =cut
529              
530             sub bleedbox {
531 9     9 1 7112 return _bbox('BleedBox', @_);
532             }
533              
534             #=head4 get_bleedbox
535             #
536             # ($llx,$lly, $urx,$ury) = $page->get_bleedbox()
537             #
538             #=over
539             #
540             #Gets the Bleed Box based on best estimates or the default.
541             #
542             #This method is B<Deprecated>, and has been B<removed>. Use
543             #the global (C<$pdf>) or page (C<$page>) bleedbox() call with no parameters
544             #instead.
545             #
546             #=back
547             #
548             #=cut
549             #
550             #sub get_bleedbox {
551             # my $self = shift();
552             # return $self->bleedbox();
553             #}
554              
555             =head3 trimbox
556              
557             $page->trimbox($alias)
558              
559             $page->trimbox($alias, 'orient' => 'orientation')
560              
561             $page->trimbox($w,$h)
562              
563             $page->trimbox($llx,$lly, $urx,$ury)
564              
565             ($llx,$lly, $urx,$ury) = $page->trimbox()
566              
567             =over
568              
569             Sets or gets the Trim Box for this one page.
570             See L<PDF::Builder::Docs/Trim Box> for more information.
571             The method always returns the current bounds (after any set operation).
572              
573             =back
574              
575             =cut
576              
577             sub trimbox {
578 9     9 1 6027 return _bbox('TrimBox', @_);
579             }
580              
581             #=head4 get_trimbox
582             #
583             # ($llx,$lly, $urx,$ury) = $page->get_trimbox()
584             #
585             #=over
586             #
587             #Gets the Trim Box based on best estimates or the default.
588             #
589             #This method is B<Deprecated>, and has been B<removed>. Use
590             #the global (C<$pdf>) or page (C<$page>) trimbox() call with no parameters
591             #instead.
592             #
593             #=back
594             #
595             #=cut
596             #
597             #sub get_trimbox {
598             # my $self = shift();
599             # return $self->trimbox();
600             #}
601              
602             =head3 artbox
603              
604             $page->artbox($alias)
605              
606             $page->artbox($alias, 'orient' => 'orientation')
607              
608             $page->artbox($w,$h)
609              
610             $page->artbox($llx,$lly, $urx,$ury)
611              
612             ($llx,$lly, $urx,$ury) = $page->artbox()
613              
614             =over
615              
616             Sets or gets the Art Box for this one page.
617             See L<PDF::Builder::Docs/Art Box> for more information.
618             The method always returns the current bounds (after any set operation).
619              
620             =back
621              
622             =cut
623              
624             sub artbox {
625 9     9 1 7987 return _bbox('ArtBox', @_);
626             }
627              
628             #=head4 get_artbox
629             #
630             # ($llx,$lly, $urx,$ury) = $page->get_artbox()
631             #
632             #=over
633             #
634             #Gets the Art Box based on best estimates or the default.
635             #
636             #This method is B<Deprecated>, and has been B<removed>. Use
637             #the global (C<$pdf>) or page (C<$page>) artbox() call with no parameters
638             #instead.
639             #
640             #=back
641             #
642             #=cut
643             #
644             #sub get_artbox {
645             # my $self = shift();
646             # return $self->artbox();
647             #}
648              
649             =head3 size
650              
651             $page->size($size) # Set
652              
653             @rectangle = $page->size() # Get
654              
655             =over
656              
657             Set the physical page size or return the coordinates of the rectangle enclosing
658             the physical page size.
659             This is an alternate method provided for compatibility with PDF::API2.
660              
661             # Set the physical page (media) size using a common size name
662             $page->size('letter');
663              
664             # Set the page size using coordinates in points (X1, Y1, X2, Y2)
665             $page->size([0, 0, 612, 792]);
666              
667             # Get the page coordinates in points
668             my @rectangle = $page->size();
669              
670             See Page Sizes above for possible values.
671             The size method is a convenient shortcut for setting the PDF's media box when
672             other prepress (print-related) page boundaries aren't required. It's equivalent
673             to the following:
674              
675             # Set
676             $page = $page->boundaries('media' => $size);
677              
678             # Get
679             @rectangle = $page->boundaries()->{'media'}->@*;
680              
681             =back
682              
683             =cut
684            
685             sub size {
686 1     1 1 4981 my $self = shift;
687              
688 1 50       6 if (@_) { # Set (also returns object, for easy chaining)
689 1         7 return $self->boundaries('media' => @_);
690             } else { # Get
691 0         0 my %boundaries = $self->boundaries();
692 0         0 return @{ $boundaries{'media'} };
  0         0  
693             }
694             }
695              
696             =head3 boundaries
697              
698             $page = $page->boundaries(%boundaries)
699              
700             %boundaries = $page->boundaries()
701              
702             =over
703              
704             Set prepress page boundaries to facilitate printing. Returns the current page
705             boundaries if called without arguments.
706             This is an alternate method provided for compatibility with PDF::API2.
707              
708             # Set
709             %boundaries = $page->boundaries(
710             # 13x19 inch physical sheet size
711             'media' => '13x19',
712             # sheet content is 11x17 with 0.25" bleed
713             'bleed' => [0.75 * 72, 0.75 * 72, 12.25 * 72, 18.25 * 72],
714             # 11x17 final trimmed size
715             'trim' => 0.25 * 72,
716             );
717              
718             The C<%boundaries> hash contains one or more page boundary keys (see Page
719             Boundaries) to set or replace, each with a corresponding size (see Page Sizes).
720             The resulting boundaries hash will be returned.
721              
722             # Get
723             %boundaries = $page->boundaries();
724             ($x1,$y1, $x2,$y2) = $page->boundaries('trim');
725              
726             If called without arguments, the returned hashref will contain (Get) all five
727             boundaries. If called with one string argument, it returns the coordinates for
728             the specified page (media, crop, bleed, trim, or art) boundary. If more than
729             one boundary type is given, only the first is processed, and a warning is
730             given that the remainder are ignored.
731              
732             =back
733              
734             =cut
735              
736             sub boundaries {
737 5     5 1 12649 my $self = shift();
738              
739             # Get
740 5 50       62 if (@_ == 0) { # empty list -- do all boxes
    50          
741 0         0 my %boundaries;
742 0         0 foreach my $box (qw( Media Crop Bleed Trim Art )) {
743 0         0 my @xxx = $self->_bounding_box($box . 'Box');
744 0         0 $boundaries{lc($box)} = \@xxx;
745             }
746 0         0 return %boundaries;
747             } elsif (@_ == 1) { # request one specific box
748 0         0 my $box = shift();
749             # apparently it is normal to have an array of boxes, and use only first
750             #if (@_) { # more than one box in arg list?
751             # carp "More than one box requested for boundaries(). Using only first ($box)";
752             #}
753 0         0 my @coordinates = $self->_bounding_box(ucfirst($box) . 'Box');
754 0         0 return @coordinates;
755             }
756              
757             # Set
758 5         30 my %boxes = @_; # should be even count
759 5         14 foreach my $box (qw( media crop bleed trim art )) {
760 25 100       70 next unless exists $boxes{$box};
761              
762             # Special case: A single number as the value for anything other than
763             # MediaBox means to take the next larger size and reduce it by this
764             # amount in points on all four sides, provided the larger size was also
765             # included.
766 7         17 my $value = $boxes{$box};
767 7         13 my @rectangle;
768 7 100 66     59 if ($box ne 'media' and not ref($value) and looks_like_number($value)) {
      66        
769 2 50       33 my $parent = ($box eq 'crop' ? 'media' :
    50          
    50          
770             $box eq 'bleed' ? 'media' :
771             $box eq 'trim' ? 'bleed' : 'trim');
772 2 50 33     11 $parent = 'bleed' if $parent eq 'trim' and not $boxes{'trim'};
773 2 50 33     16 $parent = 'media' if $parent eq 'bleed' and not $boxes{'bleed'};
774 2 50 33     10 $parent = 'media' if $parent eq 'bleed' and not $boxes{'bleed'};
775 2 50       7 unless ($boxes{$parent}) {
776 0         0 croak "Single-number argument for $box requires $parent";
777             }
778              
779 2         4 @rectangle = @{$boxes{$parent}};
  2         7  
780 2         6 $rectangle[0] += $value;
781 2         5 $rectangle[1] += $value;
782 2         3 $rectangle[2] -= $value;
783 2         6 $rectangle[3] -= $value;
784             } else {
785 5         23 @rectangle = _to_rectangle($value);
786             }
787              
788 7         22 my $box_name = ucfirst($box) . 'Box';
789 7         36 $self->_bounding_box($box_name, @rectangle);
790 7         32 $boxes{$box} = [@rectangle];
791             }
792              
793 5         29 return %boxes;
794             }
795              
796             sub _bounding_box {
797 8     8   5013 my $self = shift();
798 8         17 my $type = shift();
799              
800             # Get
801 8 100       23 unless (scalar @_) {
802 4         23 my $box = $self->find_prop($type);
803 4 50       13 unless ($box) {
804             # Default to letter (for historical PDF::API2 reasons, not per the
805             # PDF specification)
806 0 0       0 return (0, 0, 612, 792) if $type eq 'MediaBox';
807              
808             # Use defaults per PDF 1.7 section 14.11.2 Page Boundaries
809 0 0       0 return $self->_bounding_box('MediaBox') if $type eq 'CropBox';
810 0         0 return $self->_bounding_box('CropBox');
811             }
812 4         14 return map { $_->val() } $box->elements();
  16         270  
813             }
814              
815             # Set
816 4         29 $self->{$type} = PDFArray(map { PDFNum(float($_)) } page_size(@_));
  16         47  
817 4         13 return $self;
818             }
819              
820             sub fixcontents {
821 159     159 0 480 my ($self) = @_;
822              
823 159   66     1071 $self->{'Contents'} = $self->{'Contents'} || PDFArray();
824 159 50       891 if (ref($self->{'Contents'}) =~ /Objind$/) {
825 0         0 $self->{'Contents'}->realise();
826             }
827 159 50       1119 if (ref($self->{'Contents'}) !~ /Array$/) {
828 0         0 $self->{'Contents'} = PDFArray($self->{'Contents'});
829             }
830 159         369 return;
831             }
832              
833             sub content {
834 152     152 0 430 my ($self, $obj, $dir) = @_;
835              
836 152 50 33     800 if (defined($dir) && $dir > 0) {
837 0         0 $self->precontent($obj);
838             } else {
839 152         654 $self->addcontent($obj);
840             }
841 152 50       719 $self->{' apipdf'}->new_obj($obj) unless $obj->is_obj($self->{' apipdf'});
842 152         430 $obj->{' apipdf'} = $self->{' apipdf'};
843 152         442 $obj->{' api'} = $self->{' api'};
844 152         449 $obj->{' apipage'} = $self;
845              
846 152         377 weaken $obj->{' apipdf'};
847 152         536 weaken $obj->{' api'};
848 152         550 weaken $obj->{' apipage'};
849              
850 152         327 return $obj;
851             }
852              
853             sub addcontent {
854 152     152 0 458 my ($self, @objs) = @_;
855              
856 152         900 $self->fixcontents();
857 152         684 $self->{'Contents'}->add_elements(@objs);
858 152         298 return;
859             }
860              
861             sub precontent {
862 0     0 0 0 my ($self, @objs) = @_;
863              
864 0         0 $self->fixcontents();
865 0         0 unshift(@{$self->{'Contents'}->val()}, @objs);
  0         0  
866 0         0 return;
867             }
868              
869             =head2 Page Operation Methods
870              
871             =head3 gfx, graphics
872              
873             $gfx = $page->gfx(%opts)
874              
875             $gfx = $page->gfx($prepend)
876              
877             $gfx = $page->gfx()
878              
879             =over
880              
881             Returns a graphics content object, for drawing paths and shapes.
882              
883             You may specify the "prepend" flag in the old or new way. The old way is to
884             give a single boolean value (0 false, non-zero true). The new way is to give
885             a hash element named 'prepend', with the same values.
886              
887             =over
888              
889             =item gfx(boolean_value $prepend)
890              
891             =item gfx('prepend' => boolean_value)
892              
893             =back
894              
895             If $prepend is I<true>, or the option 'prepend' is given with a I<true> value,
896             the content will be prepended to the page description (at the beginning of
897             the page's content stream).
898             Otherwise, it will be appended.
899             The default is I<false>.
900              
901             =over
902              
903             =item gfx('compress' => boolean_value)
904              
905             =back
906              
907             You may specify a compression flag saying whether the drawing instructions
908             are to be compressed. If not given, the default is for the overall PDF
909             compression setting to be used (I<on> by default).
910              
911             You may have more than one I<gfx> object. They and I<text> objects will be
912             output as objects and streams I<in the order B<that the objects are>
913             defined>, with all actions pertaining
914             to this I<gfx> object appearing in one stream. However, note that graphics
915             and text objects are not fully independent of each other: the exit state
916             (linewidth, strokecolor, etc.) of one object is the entry state of the next
917             object in line to be output, and so on.
918              
919             If you intermix multiple I<gfx> and I<text> objects on
920             a page, the results may be confusing. Say you have $gfx1, $text1, $gfx2, and
921             $text2 on your page (I<created in that order>). PDF::Builder will output all the
922             $gfx1->I<action> calls in one stream, then all the $text1->I<action> calls in
923             the next stream, and likewise for $gfx2 usage and finally $text2.
924              
925             Then it's PDF's turn to confuse you. PDF will process the entire $gfx1 object
926             stream, accumulating the graphics state to the end of the stream, and using
927             that as the entry state into $text1. In a similar manner, $gfx2 and $text2 are
928             read, processed, and rendered. Thus, a change in, say, the dash pattern in the
929             middle of $gfx1, I<after> you have output some $gfx2, $text1, and $text2
930             material, may suddenly show up at the beginning of $text1 (and continue through
931             $gfx2 and $text2)!
932              
933             It is possible to use multiple graphics objects, to avoid having to change
934             settings constantly, but you may want to consider resetting all your settings
935             at the first call to each object, so that you are starting from a known base.
936             This may most easily be done by using $I<type>->restore() and ->save() just
937             after creating $I<type>:
938              
939             $text1 = $page->text();
940             $text1->save();
941             $grfx1 = $page->gfx();
942             $grfx1->restore();
943             $grfx1->save();
944             $text2 = $page->text();
945             $text2->restore();
946             $text2->save();
947             $grfx2 = $page->gfx();
948             $grfx1->restore();
949              
950             I<Generally,> you will want to define the graphics object first, and then the
951             text object after that. This defines the order in which the streams will be
952             rendered (graphics first), which is usually desirable if you're setting a
953             background color, or have other graphics with which you want to overlay text
954             over. Sometimes, though, you may wish to overlay text with graphics, in which
955             case you might either define the objects text and then graphics, or define a
956             second graphics stream to lay over the text. Most of the time it really doesn't
957             matter which comes first (as text and graphics don't interact or overlay), but
958             in any case, be aware of states carried over from the end of one stream
959             into the next.
960              
961             B<Alternate name:> C<graphics>
962              
963             This has been added for PDF::API2 compatibility.
964              
965             =back
966              
967             =cut
968              
969 3     3 1 12 sub graphics { return gfx(@_); } ## no critic
970              
971             sub gfx {
972 133     133 1 812 my ($self, @params) = @_;
973              
974 133         277 my ($prepend, $compress);
975 133         360 $prepend = $compress = 0; # default for both is False
976 133 50       499 if (scalar @params == 0) {
    0          
    0          
977             # gfx() call. no change
978             } elsif (scalar @params == 1) {
979             # one scalar value, $prepend
980 0         0 $prepend = $params[0];
981             } elsif ((scalar @params)%2) {
982             # odd number of values, can't be a hash list
983 0         0 carp "Invalid parameters passed to gfx or graphics call!";
984             } else {
985             # hash list with at least one element
986 0         0 my %hash = @params;
987             # copy dashed hash names to preferred undashed names
988 0 0 0     0 if (defined $hash{'-prepend'} && !defined $hash{'prepend'}) { $hash{'prepend'} = delete($hash{'-prepend'}); }
  0         0  
989 0 0 0     0 if (defined $hash{'-compress'} && !defined $hash{'compress'}) { $hash{'compress'} = delete($hash{'-compress'}); }
  0         0  
990              
991 0 0       0 if (defined $hash{'prepend'}) { $prepend = $hash{'prepend'}; }
  0         0  
992 0 0       0 if (defined $hash{'compress'}) { $compress = $hash{'compress'}; }
  0         0  
993             }
994 133 50       399 if ($prepend) { $prepend = 1; }
  0         0  
995 133 100 66     1324 if ($self->{' api'}->{'forcecompress'} eq 'flate' ||
996 11         27 $self->{' api'}->{'forcecompress'} =~ m/^[1-9]\d*$/) { $compress = 1; }
997              
998 133         1675 my $gfx = PDF::Builder::Content->new();
999 133 100       540 $gfx->compressFlate() if $compress;
1000 133         804 $self->content($gfx, $prepend);
1001              
1002 133         819 return $gfx;
1003             }
1004              
1005             =head3 text
1006              
1007             $text = $page->text(%opts)
1008              
1009             $text = $page->text($prepend)
1010              
1011             $text = $page->text()
1012              
1013             =over
1014              
1015             Returns a text content object, for writing text.
1016             See L<PDF::Builder::Content> for details.
1017              
1018             You may specify the "prepend" flag in the old or new way. The old way is to
1019             give a single boolean value (0 false, non-zero true). The new way is to give
1020             a hash element named 'prepend', with the same values.
1021              
1022             =over
1023              
1024             =item text(boolean_value $prepend)
1025              
1026             =item text('prepend' => boolean_value)
1027              
1028             =back
1029              
1030             If $prepend is I<true>, or the option 'prepend' is given with a I<true> value,
1031             the content will be prepended to the page description (at the beginning of
1032             the page's content stream).
1033             Otherwise, it will be appended.
1034             The default is I<false>.
1035              
1036             =over
1037              
1038             =item text('compress' => boolean_value)
1039              
1040             =back
1041              
1042             You may specify a compression flag saying whether the text content is
1043             to be compressed. If not given, the default is for the overall PDF
1044             compression setting to be used (I<on> by default).
1045              
1046             Please see the discussion above in C<gfx()> regarding multiple graphics and
1047             text objects on one page, how they are grouped into PDF objects and streams,
1048             and the rendering consequences of running through one entire object (stream)
1049             at a time, before moving on to the next. Even if you have only one graphics
1050             and one text stream, the order in which they are defined has consequences for
1051             how text overlays graphics or vice-versa.
1052              
1053             The I<text> object has many settings and attributes of its own, but shares many
1054             with graphics (I<gfx>), such as strokecolor, fillcolor, linewidth, linedash,
1055             and the like. Thus there is some overlap in attributes, and graphics and text
1056             calls can affect each other.
1057              
1058             =back
1059              
1060             =cut
1061              
1062             sub text {
1063 19     19 1 1302 my ($self, @params) = @_;
1064              
1065 19         42 my ($prepend, $compress);
1066 19         59 $prepend = $compress = 0; # default for both is False
1067 19 50       85 if (scalar @params == 0) {
    0          
    0          
1068             # text() call. no change
1069             } elsif (scalar @params == 1) {
1070             # one scalar value, $prepend
1071 0         0 $prepend = $params[0];
1072             } elsif ((scalar @params)%2) {
1073             # odd number of values, can't be a hash list
1074 0         0 carp "Invalid parameters passed to text() call!";
1075             } else {
1076             # hash list with at least one element
1077 0         0 my %hash = @params;
1078             # copy dashed hash names to preferred undashed names
1079 0 0 0     0 if (defined $hash{'-prepend'} && !defined $hash{'prepend'}) { $hash{'prepend'} = delete($hash{'-prepend'}); }
  0         0  
1080 0 0 0     0 if (defined $hash{'-compress'} && !defined $hash{'compress'}) { $hash{'compress'} = delete($hash{'-compress'}); }
  0         0  
1081              
1082 0 0       0 if (defined $hash{'prepend'}) { $prepend = $hash{'prepend'}; }
  0         0  
1083 0 0       0 if (defined $hash{'compress'}) { $compress = $hash{'compress'}; }
  0         0  
1084             }
1085 19 50       71 if ($prepend) { $prepend = 1; }
  0         0  
1086 19 100 66     181 if ($self->{' api'}->{'forcecompress'} eq 'flate' ||
1087 5         10 $self->{' api'}->{'forcecompress'} =~ m/^[1-9]\d*$/) { $compress = 1; }
1088              
1089 19         278 my $text = PDF::Builder::Content::Text->new();
1090 19 100       93 $text->compressFlate() if $compress;
1091 19         106 $self->content($text, $prepend);
1092              
1093 19         93 return $text;
1094             }
1095              
1096             =head3 rotate, rotation
1097              
1098             $page->rotate($deg)
1099              
1100             =over
1101              
1102             Rotates the page by the given degrees, which must be a multiple of 90.
1103             An angle that is not a multiple of 90 will be rounded to the nearest 90
1104             degrees, with a message.
1105              
1106             Note that the rotation angle is I<clockwise> for a positive amount!
1107             E.g., a rotation of +90 (or -270) will have the bottom edge of the paper at
1108             the left of the screen.
1109             After rotating the page 180 degrees, C<[0, 0]> (originally lower left corner)
1110             will be be in the top right corner of the page, rather than the bottom left.
1111             X will increase to the right, and Y will increase downward.
1112              
1113             (This allows you to auto-rotate to landscape without changing the mediabox!
1114             There are other ways to accomplish this end, such as using the C<size()>
1115             method, which will not change the coordinate system (move the origin).)
1116              
1117             B<Note> that some users have reported problems with using C<rotate>, that
1118             the dimensions were limited to the smaller of the original height or width. If
1119             you experience this, be sure to check whether you are doing some sort of I<crop
1120             box> or other clipping, that might not rotate as expected with the rest of the
1121             page. In other words, you might need to manually adjust the crop box dimensions.
1122              
1123             Do not confuse this C<rotate()> call with the I<graphics context> rotation
1124             (Content.pm) C<rotate()>, which permits any angle, is of opposite direction,
1125             and does not shift the origin!
1126              
1127             B<Alternate name:> C<rotation>
1128              
1129             This has been added for PDF::API2 compatibility.
1130              
1131             =back
1132              
1133             =cut
1134              
1135 0     0 1 0 sub rotation { return rotate(@_); } ## no critic
1136              
1137             sub rotate {
1138 0     0 1 0 my ($self, $degrees) = @_;
1139              
1140 0   0     0 $degrees //= 0;
1141             # Ignore rotation of 360 or more (in either direction)
1142 0         0 $degrees = $degrees % 360; # range [0, 360)
1143              
1144 0 0       0 if ($degrees % 90) {
1145 0         0 my $deg = int(($degrees + 45)/90)*90;
1146 0         0 carp "page rotate($degrees) invalid, not multiple of 90 degrees.\nChanged to $deg";
1147 0         0 $degrees = $deg;
1148             }
1149              
1150 0         0 $self->{'Rotate'} = PDFNum($degrees);
1151              
1152 0         0 return $self;
1153             }
1154              
1155             =head3 object
1156              
1157             $page = $page->object($object, $x,$y, $scale_x,$scale_y)
1158              
1159             =over
1160              
1161             Places an image or other external object (a.k.a. XObject) on the page in the
1162             specified location.
1163              
1164             If C<$x> and C<$y> are omitted, the object will be placed at C<[0, 0]>.
1165              
1166             For images, C<$scale_x> and C<$scale_y> represent the width and height of the
1167             image on the page in points. If C<$scale_x> is omitted, it will default to 72
1168             pixels per inch. If C<$scale_y> is omitted, the image will be scaled
1169             proportionally based on the image dimensions.
1170              
1171             For other external objects, the scale is a multiplier, where 1 (the default)
1172             represents 100% (i.e. no change).
1173              
1174             If the object to be placed depends on a coordinate transformation (e.g. rotation
1175             or skew), first create a content object using L</"graphics">, then call
1176             L<PDF::Builder::Content/"object"> after making the appropriate transformations.
1177              
1178             =back
1179              
1180             =cut
1181              
1182             sub object {
1183 0     0 1 0 my $self = shift();
1184 0         0 $self->graphics()->object(@_);
1185 0         0 return $self;
1186             }
1187              
1188             =head3 annotation
1189              
1190             $ant = $page->annotation()
1191              
1192             =over
1193              
1194             Returns a new annotation object.
1195              
1196             =back
1197              
1198             =cut
1199              
1200             sub annotation {
1201 6     6 1 67 my $self = shift;
1202              
1203 6 100 33     25 unless (exists $self->{'Annots'}) {
1204 5         19 $self->{'Annots'} = PDFArray();
1205 5         17 $self->{' apipdf'}->out_obj($self);
1206             } elsif (ref($self->{'Annots'}) =~ /Objind/) {
1207             $self->{'Annots'}->realise();
1208             }
1209              
1210 6         925 require PDF::Builder::Annotation;
1211 6         58 my $ant = PDF::Builder::Annotation->new();
1212 6         30 $self->{'Annots'}->add_elements($ant);
1213 6         35 $self->{' apipdf'}->new_obj($ant);
1214 6         22 $ant->{' apipdf'} = $self->{' apipdf'};
1215 6         19 $ant->{' apipage'} = $self;
1216 6         12 weaken $ant->{' apipdf'};
1217 6         16 weaken $ant->{' apipage'};
1218              
1219 6 100       28 if ($self->{'Annots'}->is_obj($self->{' apipdf'})) {
1220 1         4 $self->{' apipdf'}->out_obj($self->{'Annots'});
1221             }
1222              
1223 6         36 return $ant;
1224             }
1225              
1226             =head3 resource
1227              
1228             $page->resource($type, $key, $obj)
1229              
1230             =over
1231              
1232             Adds a resource to the page-inheritance tree.
1233              
1234             B<Example:>
1235              
1236             $co->resource('Font', $fontkey, $fontobj);
1237             $co->resource('XObject', $imagekey, $imageobj);
1238             $co->resource('Shading', $shadekey, $shadeobj);
1239             $co->resource('ColorSpace', $spacekey, $speceobj);
1240              
1241             B<Note:> You only have to add the required resources if
1242             they are NOT handled by the *font*, *image*, *shade* or *space*
1243             methods.
1244              
1245             =back
1246              
1247             =cut
1248              
1249             sub resource {
1250 32     32 1 130 my ($self, $type, $key, $obj, $force) = @_;
1251              
1252 32         197 my $dict = $self->find_prop('Resources');
1253              
1254 32   0     107 $dict = $dict || $self->{'Resources'} || PDFDict();
1255              
1256 32 50       1317 $dict->realise() if ref($dict) =~ /Objind$/;
1257              
1258 32   66     214 $dict->{$type} = $dict->{$type} || PDFDict();
1259 32 50       168 $dict->{$type}->realise() if ref($dict->{$type}) =~ /Objind$/;
1260              
1261 32 100       100 unless (defined $obj) {
1262 2   50     33 return $dict->{$type}->{$key} || undef;
1263             } else {
1264 30 50       145 if ($force) {
1265 0         0 $dict->{$type}->{$key} = $obj;
1266             } else {
1267 30   33     231 $dict->{$type}->{$key} = $dict->{$type}->{$key} || $obj;
1268             }
1269              
1270 30 50       177 $self->{' apipdf'}->out_obj($dict) if $dict->is_obj($self->{' apipdf'});
1271 30 50       129 $self->{' apipdf'}->out_obj($dict->{$type}) if $dict->{$type}->is_obj($self->{' apipdf'});
1272 30 100       133 $self->{' apipdf'}->out_obj($obj) if $obj->is_obj($self->{' apipdf'});
1273 30         164 $self->{' apipdf'}->out_obj($self);
1274              
1275 30         114 return $dict;
1276             }
1277             }
1278              
1279             sub ship_out {
1280 0     0 0   my ($self, $pdf) = @_;
1281              
1282 0           $pdf->ship_out($self);
1283 0 0         if (defined $self->{'Contents'}) {
1284 0           $pdf->ship_out($self->{'Contents'}->elements());
1285             }
1286 0           return $self;
1287             }
1288              
1289             #sub outobjdeep {
1290             # my ($self, @opts) = @_;
1291             #
1292             # foreach my $k (qw/ api apipdf /) {
1293             # $self->{" $k"} = undef;
1294             # delete($self->{" $k"});
1295             # }
1296             # return $self->SUPER::outobjdeep(@opts);
1297             #}
1298              
1299             1;