File Coverage

blib/lib/PDF/Cropmarks.pm
Criterion Covered Total %
statement 278 282 98.5
branch 77 94 81.9
condition 20 24 83.3
subroutine 42 42 100.0
pod 1 1 100.0
total 418 443 94.3


line stmt bran cond sub pod time code
1             package PDF::Cropmarks;
2              
3 5     5   1019932 use utf8;
  5         31  
  5         39  
4 5     5   221 use strict;
  5         10  
  5         166  
5 5     5   39 use warnings;
  5         14  
  5         202  
6              
7 5     5   4092 use Moo;
  5         82777  
  5         41  
8 5     5   13581 use Types::Standard qw/Maybe Str Object Bool StrictNum Int HashRef ArrayRef/;
  5         425607  
  5         76  
9 5     5   12615 use File::Copy;
  5         14353  
  5         525  
10 5     5   48 use File::Spec;
  5         11  
  5         141  
11 5     5   3098 use File::Temp;
  5         48686  
  5         570  
12 5     5   1674 use PDF::API2;
  5         464900  
  5         243  
13 5     5   100 use PDF::API2::Util;
  5         8  
  5         955  
14 5     5   70 use POSIX qw();
  5         10  
  5         126  
15 5     5   26 use File::Basename qw/fileparse/;
  5         8  
  5         438  
16 5     5   3917 use namespace::clean;
  5         73650  
  5         43  
17 5     5   7604 use Data::Dumper;
  5         29976  
  5         678  
18             use constant {
19             DEBUG => !!$ENV{AMW_DEBUG},
20 5     5   76 };
  5         11  
  5         26859  
21              
22             =encoding utf8
23              
24             =head1 NAME
25              
26             PDF::Cropmarks - Add cropmarks to existing PDFs
27              
28             =head1 VERSION
29              
30             Version 0.11
31              
32             =cut
33              
34             our $VERSION = '0.11';
35              
36             =head1 SYNOPSIS
37              
38             This module prepares PDF for printing adding the cropmarks, usually on
39             a larger physical page, doing the same thing the LaTeX package "crop"
40             does. It also takes care of the paper thickness, shifting the logical
41             pages to compensate the folding.
42              
43             It comes with a ready-made script, C. E.g.
44              
45             $ pdf-cropmarks.pl --help # usage
46             $ pdf-cropmarks.pl --paper a3 input.pdf output.pdf
47              
48             To use the module in your code:
49              
50             use strict;
51             use warnings;
52             use PDF::Cropmarks;
53             PDF::Cropmarks->new(input => $input,
54             output => $output,
55             paper => $paper,
56             # other options here
57             )->add_cropmarks;
58              
59             If everything went well (no exceptions thrown), you will find the new
60             pdf in the output you provided.
61              
62             =head1 ACCESSORS
63              
64             The following options need to be passed to the constructor and are
65             read-only.
66              
67             =head2 input
68              
69             The filename of the input. Required.
70              
71             =head2 output
72              
73             The filename of the output. Required.
74              
75             =head2 paper
76              
77             This module each logical page of the original PDF into a larger
78             physical page, adding the cropmarks in the margins. With this option
79             you can control the dimension of the output paper.
80              
81             You can specify the dimension providing a (case insensitive) string
82             with the paper name (2a, 2b, 36x36, 4a, 4b, a0, a1, a2, a3, a4, a5,
83             a6, b0, b1, b2, b3, b4, b5, b6, broadsheet, executive, ledger, legal,
84             letter, tabloid) or a string with width and height separated by a
85             column, like C<11cm:200mm>. Supported units are mm, in, pt and cm.
86              
87             An exception is thrown if the module is not able to parse the input
88             provided.
89              
90             =head2 Positioning
91              
92             The following options control where the logical page is put on the
93             physical one. They all default to true, meaning that the logical page
94             is centered. Setting top and bottom to false, or inner and outer to
95             false makes no sense (you achieve the same result specifing a paper
96             with the same width or height) and thus ignored, resulting in a
97             centering.
98              
99             =over 4
100              
101             =item top
102              
103             =item bottom
104              
105             =item inner
106              
107             =item outer
108              
109             =back
110              
111             =head2 twoside
112              
113             Boolean, defaults to true.
114              
115             This option affects the positioning, if inner or outer are set to
116             false. If C is true (default), inner margins are considered
117             the left ones on an the recto pages (the odd-numbered ones). If set to
118             false, the left margin is always considered the inner one.
119              
120             =head2 cropmark_length
121              
122             Default: 12mm
123              
124             The length of the cropmark line.
125              
126             =head2 cropmark_offset
127              
128             Default: 3mm
129              
130             The distance from the logical page corner and the cropmark line.
131              
132             =head2 font_size
133              
134             Default: 8pt
135              
136             The font size of the headers and footers with the job name, date, and
137             page numbers.
138              
139             =head2 signature
140              
141             Default to 0, meaning that no signature is needed. If set to 1, means
142             that all the pages should fit in a single signature, otherwise it
143             should be a multiple of 4.
144              
145             =head2 paper_thickness
146              
147             When passing the signature option, the logical pages are shifted on
148             the x axys by this amount to compensate the paper folding. Accept a
149             measure.
150              
151             This option is active only when the signature is set (default to
152             false) and twoside is true (the default). Default to 0.1mm, which is
153             appropriate for the common paper 80g/m2. You can do the math measuring
154             a stack height and dividing by the number of sheets.
155              
156             =head2 title
157              
158             The (optional) job title to put on the markers. It defaults to the
159             file basename.
160              
161             =head2 cover
162              
163             Relevant if signature is passed. Usually the last signature is filled
164             with blank pages until it's full. With this option turned on, the last
165             page of the document is moved to the end of the stack. If you have 13
166             pages, and a signature of 4, you will end up with 16 pages with
167             cropmarks, and the last three empty. With this option you will have
168             page 16 with the logical page 13 on it, while the pages 13-14-15 will
169             be empty (but with cropmarks nevertheless).
170              
171             =cut
172              
173             has cropmark_length => (is => 'ro', isa => Str, default => sub { '12mm' });
174              
175             has cropmark_offset => (is => 'ro', isa => Str, default => sub { '3mm' });
176              
177             has font_size => (is => 'ro', isa => Str, default => sub { '8pt' });
178              
179             has cropmark_length_in_pt => (is => 'lazy', isa => StrictNum);
180             has cropmark_offset_in_pt => (is => 'lazy', isa => StrictNum);
181             has font_size_in_pt => (is => 'lazy', isa => StrictNum);
182             has _font_object => (is => 'rw', isa => Maybe[Object]);
183             has signature => (is => 'rwp', isa => Int, default => sub { 0 });
184             has paper_thickness => (is => 'ro', isa => Str, default => sub { '0.1mm' });
185             has paper_thickness_in_pt => (is => 'lazy', isa => StrictNum);
186             has cover => (is => 'ro', isa => Bool, default => sub { 0 });
187              
188             sub _build_paper_thickness_in_pt {
189 10     10   3256 my $self = shift;
190 10         153 return $self->_string_to_pt($self->paper_thickness);
191             }
192              
193             sub _build_cropmark_length_in_pt {
194 11     11   5888 my $self = shift;
195 11         126 return $self->_string_to_pt($self->cropmark_length);
196             }
197             sub _build_cropmark_offset_in_pt {
198 11     11   4221 my $self = shift;
199 11         66 return $self->_string_to_pt($self->cropmark_offset);
200             }
201              
202             sub _build_font_size_in_pt {
203 11     11   4432 my $self = shift;
204 11         94 return $self->_string_to_pt($self->font_size);
205             }
206              
207             has thickness_page_offsets => (is => 'lazy', isa => HashRef[HashRef]);
208              
209             sub _build_thickness_page_offsets {
210 10     10   4480 my $self = shift;
211 10         281 my $total_pages = $self->total_output_pages;
212 10         313 my %out = map { $_ => 0 } (1 .. $total_pages);
  144         361  
213 10 50       80 if (my $signature = $self->signature) {
214             # convert to the real signature
215 10 100       40 if ($signature == 1) {
216 2         6 $signature = $total_pages;
217             }
218 10 50       35 die "Should have already died, signature not a multiple of four" if $signature % 4;
219 10         25 my $half = $signature / 2;
220 10         185 my $offset = $self->paper_thickness_in_pt * ($half / 2);
221 10         504 my $original_offset = $self->paper_thickness_in_pt * ($half / 2);
222 10         128 my $signature_number = 0;
223 10         37 foreach my $page (1 .. $total_pages) {
224 144   66     371 my $page_in_sig = $page % $signature || $signature;
225 144 100       275 if ($page_in_sig == 1) {
226 14         23 $offset = $original_offset;
227 14         22 $signature_number++;
228             }
229 144         119 print "page in sig / $signature_number : $page_in_sig\n" if DEBUG;
230             # odd pages triggers a stepping
231 144 100       296 if ($page_in_sig % 2) {
232 72 100       205 if ($page_in_sig > ($half + 1)) {
    100          
233 22         503 $offset += $self->paper_thickness_in_pt;
234             }
235             elsif ($page_in_sig < $half) {
236 36         841 $offset -= $self->paper_thickness_in_pt;
237             }
238             }
239 144         641 my $rounded = $self->_round($offset);
240 144         152 print "offset for page is $rounded\n" if DEBUG;
241 144         615 $out{$page} = {
242             offset => $rounded,
243             signature => $signature_number,
244             signature_page => $page_in_sig,
245             };
246             }
247             }
248 10         286 return \%out;
249             }
250              
251             has total_input_pages => (is => 'lazy', isa => Int);
252              
253             sub _build_total_input_pages {
254 18     18   5266 my $self = shift;
255 18         463 my $count = $self->in_pdf_object->pages;
256 18         1081 return $count;
257             }
258              
259             has total_output_pages => (is => 'lazy', isa => Int);
260              
261             sub _build_total_output_pages {
262 18     18   5071 my $self = shift;
263 18         456 my $total_input_pages = $self->total_input_pages;
264              
265 18 100       721 if (my $signature = $self->signature) {
266 13 100       112 if ($signature == 1) {
    100          
267             # all the pages on a single signature
268             # round to the next multiple of 4
269 3         9 my $missing = 0;
270 3 50       87 if (my $modulo = $total_input_pages % 4) {
271 0         0 $missing = 4 - $modulo;
272             }
273 3         114 return $total_input_pages + $missing;
274             }
275             elsif ($signature % 4) {
276 1         17 die "Signature must be 1 or a multiple of 4, but I got $signature";
277             }
278             else {
279 9         26 my $missing = 0;
280 9 100       69 if (my $modulo = $total_input_pages % $signature) {
281 6         20 $missing = $signature - $modulo;
282             }
283 9         293 return $total_input_pages + $missing;
284             }
285             }
286             else {
287 5         130 return $total_input_pages;
288             }
289             }
290              
291              
292             sub _measure_re {
293 58     58   354 return qr{([0-9]+(\.[0-9]+)?)\s*
294             (mm|in|pt|cm)}sxi;
295             }
296              
297             sub _string_to_pt {
298 47     47   128 my ($self, $string) = @_;
299             my %compute = (
300 26     26   195 mm => sub { $_[0] / (25.4 / 72) },
301 3     3   31 in => sub { $_[0] / (1 / 72) },
302 16     16   105 pt => sub { $_[0] / 1 },
303 2     2   18 cm => sub { $_[0] / (25.4 / 72) * 10 },
304 47         768 );
305 47         187 my $re = $self->_measure_re;
306 47 50       559 if ($string =~ $re) {
307 47         163 my $size = $1;
308 47         164 my $unit = lc($3);
309 47         197 return $self->_round($compute{$unit}->($size));
310             }
311             else {
312 0         0 die "Unparsable measure string $string";
313             }
314             }
315              
316             =head1 METHODS
317              
318             =head2 add_cropmarks
319              
320             This is the only public method: create the new pdf from C and
321             leave it in C.
322              
323             =cut
324              
325             has input => (is => 'ro', isa => Str, required => 1);
326              
327             has output => (is => 'ro', isa => Str, required => 1);
328              
329             has paper => (is => 'ro', isa => Str, default => sub { 'a4' });
330              
331             has _tmpdir => (is => 'ro',
332             isa => Object,
333             default => sub {
334             return File::Temp->newdir(CLEANUP => !DEBUG);
335             });
336              
337             has in_pdf => (is => 'lazy', isa => Str);
338              
339             has out_pdf => (is => 'lazy', isa => Str);
340              
341             has basename => (is => 'lazy', isa => Str);
342              
343             has timestamp => (is => 'lazy', isa => Str);
344              
345             sub _build_basename {
346 9     9   3016 my $self = shift;
347 9         557 my $basename = fileparse($self->input, qr{\.pdf}i);
348 9         201 return $basename;
349             }
350              
351             sub _build_timestamp {
352 11     11   3756 my $now = localtime();
353 11         260 return $now;
354             }
355              
356             has top => (is => 'ro', isa => Bool, default => sub { 1 });
357             has bottom => (is => 'ro', isa => Bool, default => sub { 1 });
358             has inner => (is => 'ro', isa => Bool, default => sub { 1 });
359             has outer => (is => 'ro', isa => Bool, default => sub { 1 });
360             has twoside => (is => 'ro', isa => Bool, default => sub { 1 });
361             has _is_closed => (is => 'rw', isa => Bool, default => sub { 0 });
362             sub _build_in_pdf {
363 18     18   4005 my $self = shift;
364 18         469 my $name = File::Spec->catfile($self->_tmpdir, 'in.pdf');
365 18 50       479 copy ($self->input, $name) or die "Cannot copy input to $name $!";
366 18         14336 return $name;
367             }
368              
369             sub _build_out_pdf {
370 11     11   4449 my $self = shift;
371 11         355 return File::Spec->catfile($self->_tmpdir, 'out.pdf');
372             }
373              
374              
375             has in_pdf_object => (is => 'lazy', isa => Object);
376              
377             sub _build_in_pdf_object {
378 18     18   5040 my $self = shift;
379 18         43 my $input = eval { PDF::API2->open($self->in_pdf) };
  18         437  
380 18 50       1375597 if ($input) {
381 18         901 return $input;
382             }
383             else {
384 0 0       0 die "Cannot open " . $self->in_pdf . " $@" unless $input;
385             }
386             }
387              
388             has out_pdf_object => (is => 'lazy', isa => Object);
389              
390             sub _build_out_pdf_object {
391 18     18   11194 my $self = shift;
392 18         234 my $pdf = PDF::API2->new;
393 18         16959 my $now = POSIX::strftime(q{%Y%m%d%H%M%S+00'00'}, localtime(time()));
394              
395 18         705 my %info = ($self->in_pdf_object->info,
396             Creator => "PDF::Cropmarks $VERSION",
397             CreationDate => POSIX::strftime(q{%Y%m%d%H%M%S+00'00'}, localtime((stat($self->in_pdf))[9])),
398             ModDate => POSIX::strftime(q{%Y%m%d%H%M%S+00'00'}, localtime(time())));
399 11         27076 $pdf->info(%info);
400 11         1820 $pdf->mediabox($self->_paper_dimensions);
401 11         2250 return $pdf;
402             }
403              
404             sub _paper_dimensions {
405 11     11   29 my $self = shift;
406 11         85 my $paper = $self->paper;
407 11         72 my %sizes = PDF::API2::Util::getPaperSizes();
408 11         1449 my $measure_re = $self->_measure_re;
409 11 100       203 if (my $dimensions = $sizes{lc($self->paper)}) {
    50          
410 9         141 return @$dimensions;
411             }
412             elsif ($paper =~ m/\A\s*
413             $measure_re
414             \s*:\s*
415             $measure_re
416             \s*\z/sxi) {
417             # 3 + 3 captures
418 2         9 my $xsize = $1;
419 2         7 my $xunit = $3;
420 2         8 my $ysize = $4;
421 2         5 my $yunit = $6;
422 2         12 return ($self->_string_to_pt($xsize . $xunit),
423             $self->_string_to_pt($ysize . $yunit));
424             }
425             else {
426 0         0 die "Cannot get dimensions from $paper, using A4";
427             }
428             }
429              
430             sub add_cropmarks {
431 11     11 1 15883 my $self = shift;
432 11 50       267 die "add_cropmarks already called!" if $self->_is_closed;
433 11         3566 my $needed = $self->total_output_pages - $self->total_input_pages;
434 11 50       921 die "Something is off, pages needed: $needed pages" if $needed < 0;
435 11         266 my @sequence = (1 .. $self->total_input_pages);
436 11 100       124 if ($needed) {
437 5         12 my $last;
438 5 100       43 if ($self->cover) {
439 2         10 $last = pop @sequence;
440             }
441 5         24 while ($needed > 0) {
442 16         25 push @sequence, undef;
443 16         28 $needed--;
444             }
445 5 100       25 if ($last) {
446 2         7 push @sequence, $last;
447             }
448             }
449 11         23 my $as_page_number = 0;
450 11         23 print Dumper(\@sequence) if DEBUG;
451 11         233 $self->_font_object($self->out_pdf_object->corefont('Courier'));
452 11         316351 foreach my $src_page_number (@sequence) {
453 141         105265 $as_page_number++;
454             # and set it as page_number
455 141         802 $self->_import_page($src_page_number, $as_page_number);
456             }
457 11         10467 print "Saving " . $self->out_pdf . "\n" if DEBUG;
458 11         534 $self->out_pdf_object->saveas($self->out_pdf);
459 11         1363143 $self->_cleanup;
460              
461 11 50       319 move($self->out_pdf, $self->output)
462             or die "Cannot copy " . $self->out_pdf . ' to ' . $self->output;
463              
464 11         2640 return $self->output;
465             }
466              
467             sub _round {
468 618     618   7683 my ($self, $float) = @_;
469 618         807 print "Rounding $float\n" if DEBUG;
470 618 100       1665 return 0 unless $float;
471 586 100 100     2683 if ($float < 0.001 && $float > -0.001) {
472 28         82 return 0;
473             }
474 558         8117 return sprintf('%.3f', $float);
475             }
476              
477             has output_dimensions => (is => 'lazy', isa => ArrayRef);
478              
479             has title => (is => 'ro', isa => Maybe[Str]);
480              
481             sub _build_output_dimensions {
482 5     5   3080 my $self = shift;
483             # get the first page
484 5         170 my $in_page = $self->in_pdf_object->openpage(1);
485 5         320 return [ $in_page->get_mediabox ];
486             }
487              
488             sub _import_page {
489 141     141   307 my ($self, $src_page_number, $page_number) = @_;
490 141 100       5284 my $in_page = (defined $src_page_number ? $self->in_pdf_object->openpage($src_page_number) : undef);
491 141         378759 my $page = $self->out_pdf_object->page;
492 141         79872 my ($llx, $lly, $urx, $ury) = $page->get_mediabox;
493 141 50       11011 die "mediabox origins for output pdf should be zero" if $llx + $lly;
494 141         277 print "$llx, $lly, $urx, $ury\n" if DEBUG;
495 141 100       782 my ($inllx, $inlly, $inurx, $inury) = ($in_page ? $in_page->get_mediabox : @{$self->output_dimensions});
  16         488  
496 141         7860 print "$inllx, $inlly, $inurx, $inury\n" if DEBUG;
497 141 50       612 die "mediabox origins for input pdf should be zero" if $inllx + $inlly;
498             # place the content into page
499              
500 141         1169 my $offset_x = $self->_round(($urx - $inurx) / 2);
501 141         690 my $offset_y = $self->_round(($ury - $inury) / 2);
502              
503             # adjust offset if bottom or top are missing. Both missing doesn't
504             # make much sense
505 141         426 my ($top_middle_mark, $bottom_middle_mark,
506             $left_middle_mark, $right_middle_mark) = (1, 1, 1, 1);
507              
508 141 50 66     2244 if (!$self->bottom && !$self->top) {
    100          
    100          
509             # warn "bottom and top are both false, centering\n";
510             }
511             elsif (!$self->bottom) {
512 28         71 $offset_y = 0;
513 28         74 $bottom_middle_mark = 0;
514             }
515             elsif (!$self->top) {
516 24         68 $offset_y *= 2;
517 24         64 $top_middle_mark = 0;
518             }
519              
520 141 50 66     1924 if (!$self->inner && !$self->outer) {
    100          
    100          
521             # warn "inner and outer are both false, centering\n";
522             }
523             elsif (!$self->inner) {
524             # even pages
525 56 100 100     599 if ($self->twoside and !($page_number % 2)) {
526 22         81 $offset_x *= 2;
527 22         56 $right_middle_mark = 0;
528             }
529             else {
530 34         71 $offset_x = 0;
531 34         80 $left_middle_mark = 0;
532             }
533             }
534             elsif (!$self->outer) {
535             # odd pages
536 28 100 100     297 if ($self->twoside and !($page_number % 2)) {
537 8         16 $offset_x = 0;
538 8         20 $left_middle_mark = 0;
539             }
540             else {
541 20         64 $offset_x *= 2;
542 20         51 $right_middle_mark = 0;
543             }
544             }
545              
546 141         272 my $signature_mark = '';
547 141 100 100     1321 if ($self->signature && $self->twoside) {
548 92         3308 my $spec = $self->thickness_page_offsets->{$page_number};
549 92         1397 my $paper_thickness = $spec->{offset};
550 92 50       374 die "$page_number not defined in " . Dumper($self->thickness_page_offsets)
551             unless defined $paper_thickness;
552             # recto pages, increase
553 92 100       347 if ($page_number % 2) {
554 46         158 $offset_x += $paper_thickness;
555             }
556             # verso pages, decrease
557             else {
558 46         233 $offset_x -= $paper_thickness;
559             }
560 92         586 $signature_mark = ' #' . $spec->{signature} . '/' . $spec->{signature_page};
561             }
562              
563 141         194 print "Offsets are $offset_x, $offset_y\n" if DEBUG;
564 141 100       451 if ($in_page) {
565 125         3298 my $xo = $self->out_pdf_object->importPageIntoForm($self->in_pdf_object,
566             $src_page_number);
567 125         1787152 my $gfx = $page->gfx;
568 125         23315 $gfx->formimage($xo, $offset_x, $offset_y);
569             }
570 141         52712 if (DEBUG) {
571             my $line = $page->gfx;
572             $line->strokecolor('black');
573             $line->linewidth(0.5);
574             $line->rectxy($offset_x, $offset_y,
575             $offset_x + $inurx, $offset_y + $inury);
576             $line->stroke;
577             }
578 141         636 my $crop = $page->gfx;
579 141         22567 $crop->strokecolor('black');
580 141         22875 $crop->linewidth(0.5);
581 141         11835 my $crop_width = $self->cropmark_length_in_pt;
582 141         5395 my $crop_offset = $self->cropmark_offset_in_pt;
583             # left bottom corner
584 141         2928 $self->_draw_line($crop,
585             ($offset_x - $crop_offset, $offset_y),
586             ($offset_x - $crop_width - $crop_offset, $offset_y));
587              
588              
589 141         12049 $self->_draw_line($crop,
590             ($offset_x, $offset_y - $crop_offset),
591             ($offset_x, $offset_y - $crop_offset - $crop_width));
592              
593 141 100       11607 if ($bottom_middle_mark) {
594 113         809 $self->_draw_line($crop,
595             ($offset_x + ($inurx / 2),
596             $offset_y - $crop_offset),
597             ($offset_x + ($inurx / 2),
598             $offset_y - $crop_offset - ($crop_width / 2)));
599             }
600              
601             # right bottom corner
602 141         9522 $self->_draw_line($crop,
603             ($offset_x + $inurx + $crop_offset, $offset_y),
604             ($offset_x + $inurx + $crop_offset + $crop_width,
605             $offset_y));
606 141         11468 $self->_draw_line($crop,
607             ($offset_x + $inurx,
608             $offset_y - $crop_offset),
609             ($offset_x + $inurx,
610             $offset_y - $crop_offset - $crop_width));
611              
612 141 100       12097 if ($right_middle_mark) {
613 99         704 $self->_draw_line($crop,
614             ($offset_x + $inurx + $crop_offset,
615             $offset_y + ($inury/2)),
616             ($offset_x + $inurx + $crop_offset + ($crop_width / 2),
617             $offset_y + ($inury/2)));
618             }
619              
620             # top right corner
621 141         8404 $self->_draw_line($crop,
622             ($offset_x + $inurx + $crop_offset,
623             $offset_y + $inury),
624             ($offset_x + $inurx + $crop_offset + $crop_width,
625             $offset_y + $inury));
626              
627 141         11440 $self->_draw_line($crop,
628             ($offset_x + $inurx,
629             $offset_y + $inury + $crop_offset),
630             ($offset_x + $inurx,
631             $offset_y + $inury + $crop_offset + $crop_width));
632              
633 141 100       12001 if ($top_middle_mark) {
634 117         999 $self->_draw_line($crop,
635             ($offset_x + ($inurx / 2),
636             $offset_y + $inury + $crop_offset),
637             ($offset_x + ($inurx / 2),
638             $offset_y + $inury + $crop_offset + ($crop_width / 2)));
639             }
640              
641             # top left corner
642 141         10449 $self->_draw_line($crop,
643             ($offset_x, $offset_y + $inury + $crop_offset),
644             ($offset_x,
645             $offset_y + $inury + $crop_offset + $crop_width));
646              
647 141         12070 $self->_draw_line($crop,
648             ($offset_x - $crop_offset,
649             $offset_y + $inury),
650             ($offset_x - $crop_offset - $crop_width,
651             $offset_y + $inury));
652              
653 141 100       12200 if ($left_middle_mark) {
654 99         750 $self->_draw_line($crop,
655             ($offset_x - $crop_offset,
656             $offset_y + ($inury / 2)),
657             ($offset_x - $crop_offset - ($crop_width / 2),
658             $offset_y + ($inury / 2)));
659             }
660             # and stroke
661 141         8391 $crop->stroke;
662              
663             # then add the text
664 141         6069 my $text = $page->text;
665 141         51498 my $marker = sprintf('Pg %.4d', $page_number);
666 141         7228 $text->font($self->_font_object,
667             $self->_round($self->font_size_in_pt));
668 141         34633 $text->fillcolor('black');
669              
670             # bottom left
671 141         21546 $text->translate($offset_x - (($crop_width + $crop_offset)),
672             $offset_y - (($crop_width + $crop_offset)));
673 141         67401 $text->text($marker);
674              
675             # bottom right
676 141         36768 $text->translate($inurx + $offset_x + $crop_offset,
677             $offset_y - (($crop_width + $crop_offset)));
678 141         56123 $text->text($marker);
679              
680             # top left
681 141         24649 $text->translate($offset_x - (($crop_width + $crop_offset)),
682             $offset_y + $inury + $crop_width);
683 141         55608 $text->text($marker);
684              
685             # top right
686 141         24068 $text->translate($inurx + $offset_x + $crop_offset,
687             $offset_y + $inury + $crop_width);
688 141         55658 $text->text($marker);
689              
690 141   66     30201 my $text_marker = ($self->title || $self->basename)
691             . ' ' . $self->timestamp .
692             ' page ' . $page_number . $signature_mark;
693             # and at the top and and the bottom add jobname + timestamp
694 141         6559 $text->translate(($inurx / 2) + $offset_x,
695             $offset_y + $inury + $crop_width);
696 141         57854 $text->text_center($text_marker);
697              
698 141         119664 $text->translate(($inurx / 2) + $offset_x,
699             $offset_y - ($crop_width + $crop_offset));
700 141         56966 $text->text_center($text_marker);
701             }
702              
703             sub _draw_line {
704 1556     1556   3345 my ($self, $gfx, $from_x, $from_y, $to_x, $to_y) = @_;
705 1556         1875 if (DEBUG) {
706             print "Printing line from ($from_x, $from_y) to ($to_x, $to_y)\n";
707             }
708 1556         4774 $gfx->move($from_x, $from_y);
709 1556         140536 $gfx->line($to_x, $to_y);
710 1556         127269 my $radius = 3;
711 1556         5326 $gfx->circle($to_x, $to_y, $radius);
712 1556         4385608 $gfx->move($to_x - $radius, $to_y);
713 1556         140391 $gfx->line($to_x + $radius, $to_y);
714 1556         131376 $gfx->move($to_x, $to_y - $radius);
715 1556         129751 $gfx->line($to_x, $to_y + $radius);
716             }
717              
718             sub _cleanup {
719 29     29   96 my $self = shift;
720 29 100       1320 if ($self->_is_closed) {
721 11         511 return;
722             }
723             else {
724 18         1614 $self->_font_object(undef);
725 18         2017 $self->in_pdf_object->end;
726 18         37134 $self->out_pdf_object->end;
727 11         521 $self->_is_closed(1);
728 11         379 print "Objects closed\n" if DEBUG;
729             }
730             }
731              
732             sub DESTROY {
733 18     18   423771 my $self = shift;
734 18         121 $self->_cleanup;
735             }
736              
737             =head1 AUTHOR
738              
739             Marco Pessotto, C<< >>
740              
741             =head1 BUGS
742              
743             Please report any bugs or feature requests to the CPAN's RT or at
744             L. If you find
745             a bug, please provide a minimal example file which reproduces the
746             problem.
747              
748             =head1 LICENSE
749              
750             This program is free software; you can redistribute it and/or modify
751             it under the terms of either: the GNU General Public License as
752             published by the Free Software Foundation; or the Artistic License.
753              
754             =cut
755              
756              
757             1;