File Coverage

blib/lib/PDF/Builder/ViewerPreferences.pm
Criterion Covered Total %
statement 15 108 13.8
branch 0 82 0.0
condition n/a
subroutine 5 12 41.6
pod 4 4 100.0
total 24 206 11.6


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