File Coverage

blib/lib/PDF/Builder/ViewerPreferences.pm
Criterion Covered Total %
statement 15 113 13.2
branch 0 86 0.0
condition n/a
subroutine 5 12 41.6
pod 4 4 100.0
total 24 215 11.1


line stmt bran cond sub pod time code
1             package PDF::Builder::ViewerPreferences;
2              
3 1     1   1875 use strict;
  1         4  
  1         50  
4 1     1   7 use warnings;
  1         3  
  1         97  
5              
6             our $VERSION = '3.028'; # VERSION
7             our $LAST_UPDATE = '3.026'; # manually update whenever code is changed
8              
9 1     1   7 use Carp;
  1         2  
  1         110  
10 1     1   9 use PDF::Builder::Basic::PDF::Utils;
  1         4  
  1         111  
11 1     1   7 use Scalar::Util qw(weaken);
  1         3  
  1         2927  
12              
13             our @CARP_NOT;
14              
15             my @booleans = qw(HideToolbar HideMenubar HideWindowUI FitWindow CenterWindow
16             DisplayDocTitle PickTrayByPDFSize);
17              
18             my @names = qw(NonFullScreenPageMode Direction Duplex PrintScaling
19             ViewArea ViewClip PrintArea PrintClip);
20              
21             =head1 NAME
22              
23             PDF::Builder::ViewerPreferences - How the PDF should be displayed or printed
24              
25             =head1 METHODS
26              
27             This has been split out from C<preferences()> in L<PDF::Builder>.
28              
29             =cut
30              
31             sub _snake_case {
32 0     0     my $name = shift();
33 0           $name =~ s/^([A-Z]+)/lc($1)/e;
  0            
34 0           $name =~ s/([A-Z]+)/'_' . lc($1)/ge;
  0            
35 0           $name =~ s/pdfsize/pdf_size/;
36 0           return $name;
37             }
38              
39             sub _camel_case {
40 0     0     my $name = shift();
41 0           $name = ucfirst($name);
42 0           $name =~ s/_([a-z]+)/ucfirst($1)/ge;
  0            
43 0           $name =~ s/Ui$/UI/;
44 0           $name =~ s/Pdf/PDF/;
45 0           return $name;
46             }
47              
48             =head2 new
49              
50             $self = $class->new($pdf)
51              
52             =over
53              
54             Creates a new ViewerPreferences object from a PDF::Builder object.
55              
56             =back
57              
58             =cut
59              
60             sub new {
61 0     0 1   my ($class, $pdf) = @_;
62 0           my $self = bless { pdf => $pdf }, $class;
63 0           weaken $self->{'pdf'};
64 0           return $self;
65             }
66              
67             =head2 get_preferences
68              
69             %preferences = $self->get_preferences()
70              
71             =over
72              
73             Returns a hash containing all of the viewer preferences that are defined in the
74             PDF.
75              
76             =back
77              
78             =cut
79              
80             sub get_preferences {
81 0     0 1   my $self = shift();
82 0           my $prefs = $self->{'pdf'}->{'catalog'}->{'ViewerPreferences'};
83 0 0         return unless $prefs;
84 0           $prefs->realise();
85              
86 0           my %values;
87 0           foreach my $pref (@booleans) {
88 0 0         next unless $prefs->{$pref};
89 0 0         $values{_snake_case($pref)} = $prefs->{$pref}->val() eq 'true' ? 1 : 0;
90             }
91 0           foreach my $pref (@names) {
92 0 0         next unless $prefs->{$pref};
93 0 0         if ($pref eq 'Direction') {
    0          
    0          
94 0           $values{'direction'} = lc($prefs->{$pref}->val());
95             } elsif ($pref eq 'Duplex') {
96 0           my $value = $prefs->{$pref}->val();
97 0           $value =~ s/Flip//;
98 0           $value =~ s/Edge//;
99 0           $values{'duplex'} = _snake_case($value);
100             } elsif ($pref eq 'NonFullScreenPageMode') {
101 0           my $value = _snake_case($prefs->{$pref}->val());
102 0           $value =~ s/^use_//;
103 0 0         $value = 'optional_content' if $value eq 'oc';
104 0           $values{'non_full_screen_page_mode'} = $value;
105             } else {
106 0           $values{_snake_case($pref)} = _snake_case($prefs->{$pref}->val());
107             }
108             }
109 0 0         if ($prefs->{'PrintPageRange'}) {
110 0           my @ranges = map { $_->val() } @{$prefs->{'PrintPageRange'}};
  0            
  0            
111 0           $values{'print_page_range'} = \@ranges;
112             }
113 0 0         if ($prefs->{'NumCopies'}) {
114 0           $values{'num_copies'} = $prefs->{'NumCopies'}->val();
115             }
116              
117 0           return %values;
118             }
119              
120             =head2 get_preference
121              
122             $value = $self->get_preference($name)
123              
124             =over
125              
126             Returns the value of the specified viewer preference if present, or C<undef> if
127             not.
128              
129             =back
130              
131             =cut
132              
133             sub get_preference {
134 0     0 1   my ($self, $name) = @_;
135 0           my %values = $self->get_preferences();
136 0           return $values{$name};
137             }
138              
139             =head2 set_preferences
140              
141             $self->set_preferences(%values)
142              
143             =over
144              
145             Sets one or more viewer preferences, as described in the preferences section
146             below.
147              
148             =back
149              
150             =cut
151              
152             sub _init_preferences {
153 0     0     my $self = shift();
154 0 0         if ($self->{'pdf'}->{'catalog'}->{'ViewerPreferences'}) {
155 0           $self->{'pdf'}->{'catalog'}->{'ViewerPreferences'}->realise();
156             }
157             else {
158 0           $self->{'pdf'}->{'catalog'}->{'ViewerPreferences'} = PDFDict();
159             }
160 0           $self->{'pdf'}->{'pdf'}->out_obj($self->{'pdf'}->{'catalog'});
161 0           return $self->{'pdf'}->{'catalog'}->{'ViewerPreferences'};
162             }
163              
164             sub set_preferences {
165 0     0 1   my ($self, %values) = @_;
166 0           my $prefs = $self->_init_preferences();
167 0           local @CARP_NOT = qw(PDF::Builder);
168 0           foreach my $snake (keys %values) {
169 0           my $camel = _camel_case($snake);
170 0 0         if ($camel eq 'NonFullScreenPageMode') {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
171 0           my $value = $values{$snake};
172 0 0         my $name = ($value eq 'none' ? 'UseNone' :
    0          
    0          
    0          
173             $value eq 'outlines' ? 'UseOutlines' :
174             $value eq 'thumbnails' ? 'UseThumbs' :
175             $value eq 'optional_content' ? 'UseOC' :
176             '');
177 0 0         croak "Invalid value for $snake: $values{$snake}" unless $name;
178 0           $prefs->{$camel} = PDFName($name);
179             }
180             elsif ($camel eq 'Direction') {
181 0           my $name = $values{$snake};
182 0 0         unless ($name =~ /^(?:L2R|R2L)$/i) {
183 0           croak "Invalid value for $snake: $name";
184             }
185 0           $prefs->{$camel} = PDFName(uc $name);
186             }
187             elsif ($camel =~ /^(?:View|Print)(?:Area|Clip)$/) {
188             my $name = ($values{$snake} eq 'media_box' ? 'MediaBox' :
189             $values{$snake} eq 'crop_box' ? 'CropBox' :
190             $values{$snake} eq 'bleed_box' ? 'BleedBox' :
191             $values{$snake} eq 'trim_box' ? 'TrimBox' :
192 0 0         $values{$snake} eq 'art_box' ? 'ArtBox' : '');
    0          
    0          
    0          
    0          
193 0 0         croak "Invalid value for $snake: $name" unless $name;
194 0           $prefs->{$camel} = PDFName($name);
195             }
196             elsif ($camel eq 'PrintScaling') {
197 0           my $value = $values{$snake};
198 0 0         my $name = ($value eq 'none' ? 'None' :
    0          
199             $value eq 'app_default' ? 'AppDefault' : '');
200 0 0         croak "Invalid value for $snake: $name" unless $name;
201 0           $prefs->{$camel} = PDFName($name);
202             }
203             elsif ($camel eq 'Duplex') {
204 0           my $value = $values{$snake};
205 0 0         my $name = ($value eq 'simplex' ? 'Simplex' :
    0          
    0          
206             $value eq 'duplex_short' ? 'DuplexFlipShortEdge' :
207             $value eq 'duplex_long' ? 'DuplexFlipLongEdge' : '');
208 0 0         croak "Invalid value for $snake: $value" unless $name;
209 0           $prefs->{$camel} = PDFName($name);
210             }
211             elsif ($camel eq 'PrintPageRange') {
212 0 0         unless (ref($values{$snake}) eq 'ARRAY') {
213 0           croak "The value for $snake must be a reference to an array";
214             }
215 0           my @range = @{$values{$snake}};
  0            
216 0 0         unless (@range % 2 == 0) {
217 0           croak "The value for $snake must contain pairs of page numbers";
218             }
219 0 0         if (join('', @range) =~ /\D/) {
220 0           croak "The value for $snake may only contain page numbers";
221             }
222 0           $prefs->{$camel} = PDFArray(map { PDFNum($_) } @range);
  0            
223             }
224             elsif ($camel eq 'NumCopies') {
225 0 0         unless ($values{$snake} =~ /^\d+$/) {
226 0           croak "$snake: $values{$snake} is not an integer";
227             }
228 0           $prefs->{$camel} = PDFNum($values{$snake});
229             }
230 0           elsif (grep { $camel eq $_ } @booleans) {
231 0 0         $prefs->{$camel} = PDFBool($values{$snake} ? 1 : 0);
232             }
233             else {
234 0           croak "Unrecognized viewer preference '$snake'";
235             }
236             }
237 0           return $self;
238             }
239              
240             =head1 PREFERENCES
241              
242             Viewer Preferences describe how the document should be presented on screen or in
243             print. Not all PDF viewers will respect these preferences.
244              
245             Boolean preferences default to false and take (or return) 0 or 1 as arguments.
246              
247             Bounding Box preferences take (or return) one of C<media_box>, C<crop_box>,
248             C<bleed_box>, C<trim_box>, or C<art_box>.
249              
250             =over
251              
252             =item hide_toolbar (boolean)
253              
254             A flag specifying whether to hide the tool bars when the document is active.
255              
256             =item hide_menubar (boolean)
257              
258             A flag specifying whether to hide the menu bar when the document is active.
259              
260             =item hide_window_ui (boolean)
261              
262             A flag specifying whether to hide the user interface elements in the document's
263             window (such as scroll bars and navigation controls), leaving only the
264             document's contents displayed.
265              
266             =item fit_window (boolean)
267              
268             A flag specifying whether to resize the document's window to fit the size of the
269             first displayed page.
270              
271             =item center_window (boolean)
272              
273             A flag specifying whether to position the document's window in the center of the
274             screen.
275              
276             =item display_doc_title (boolean)
277              
278             A flag specifying whether the window's title bar should display the document
279             title taken from the Title entry of the document information directory. If
280             false, the title bar should instead display the name of the PDF file containing
281             the document.
282              
283             =item non_full_screen_page_mode (name)
284              
285             The document's page mode, specifying how to display the document on exiting
286             full-screen mode. Options are the same as C<page_mode> in L<PDF::Builder>,
287             except that the C<attachments> and C<full_screen> options aren't supported.
288              
289             =item direction ('l2r' or 'r2l')
290              
291             The predominant reading order for text (left-to-right or right-to-left).
292              
293             This entry has no direct effect on the document's contents or page numbering but
294             may be used to determine the relative positioning of pages when displayed
295             side-by-side or printed n-up.
296              
297             =item view_area (bounding box)
298              
299             The name of the page boundary representing the area of a page that shall be
300             displayed when viewing the document on the screen.
301              
302             =item view_clip (bounding box)
303              
304             The name of the page boundary to which the contents of a page shall be clipped
305             when viewing the document on the screen.
306              
307             =item print_area (bounding box)
308              
309             The name of the page boundary representing the area of a page that shall be
310             rendered when printing the document.
311              
312             =item print_clip (bounding box)
313              
314             The name of the page boundary to which the contents of a page shall be clipped
315             when printing the document.
316              
317             =item print_scaling ('none' or 'app_default')
318              
319             The page scaling option that shall be selected when a print dialog is displayed
320             for this document. C<none> represents no page scaling, and C<app_default>
321             represents the reader's default print scaling.
322              
323             =item duplex ('simplex', 'duplex_short', or 'duplex_long')
324              
325             The paper handling option that shall be used when printing the file from the
326             print dialog. The duplex values represent whether the page should be flipped on
327             its short edge or long edge, respectively.
328              
329             =item pick_tray_by_pdf_size (boolean)
330              
331             A flag specifying whether the PDF page size shall be used to select the input
332             paper tray. This setting influences only the preset values used to populate the
333             print dialog presented by the reader.
334              
335             =item print_page_rage (an array of integer pairs)
336              
337             The page numbers used to initialize the print dialog box when the file is
338             printed. The array shall contain an even number of integers to be interpreted
339             in pairs, with each pair specifying the first and last pages in a sub-range of
340             pages to be printed. The first page of the PDF file shall be denoted by 1.
341              
342             =item num_copies (integer)
343              
344             The number of copies that shall be printed when the print dialog is opened for
345             this file.
346              
347             =back
348              
349             =cut
350              
351             1;