File Coverage

blib/lib/PDF/Cropmarks.pm
Criterion Covered Total %
statement 266 270 98.5
branch 71 88 80.6
condition 24 30 80.0
subroutine 41 41 100.0
pod 1 1 100.0
total 403 430 93.7


line stmt bran cond sub pod time code
1             package PDF::Cropmarks;
2              
3 4     4   680161 use utf8;
  4         26  
  4         26  
4 4     4   122 use strict;
  4         9  
  4         153  
5 4     4   22 use warnings;
  4         15  
  4         118  
6              
7 4     4   3389 use Moo;
  4         60365  
  4         24  
8 4     4   10375 use Types::Standard qw/Maybe Str Object Bool StrictNum Int HashRef ArrayRef/;
  4         283421  
  4         45  
9 4     4   9472 use File::Copy;
  4         10065  
  4         262  
10 4     4   23 use File::Spec;
  4         8  
  4         81  
11 4     4   3611 use File::Temp;
  4         47054  
  4         355  
12 4     4   2100 use PDF::API2;
  4         468856  
  4         122  
13 4     4   66 use PDF::API2::Util;
  4         8  
  4         666  
14 4     4   19 use POSIX qw();
  4         9  
  4         76  
15 4     4   19 use File::Basename qw/fileparse/;
  4         7  
  4         282  
16 4     4   3152 use namespace::clean;
  4         50452  
  4         20  
17 4     4   5974 use Data::Dumper;
  4         19686  
  4         301  
18             use constant {
19             DEBUG => !!$ENV{AMW_DEBUG},
20 4     4   24 };
  4         8  
  4         15881  
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.07
31              
32             =cut
33              
34             our $VERSION = '0.07';
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              
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   2929 my $self = shift;
190 10         106 return $self->_string_to_pt($self->paper_thickness);
191             }
192              
193             sub _build_cropmark_length_in_pt {
194 10     10   3002 my $self = shift;
195 10         91 return $self->_string_to_pt($self->cropmark_length);
196             }
197             sub _build_cropmark_offset_in_pt {
198 10     10   2352 my $self = shift;
199 10         50 return $self->_string_to_pt($self->cropmark_offset);
200             }
201              
202             sub _build_font_size_in_pt {
203 10     10   343357 my $self = shift;
204 10         92 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   3282 my $self = shift;
211 10         249 my $total_pages = $self->total_output_pages;
212 10         194 my %out = map { $_ => 0 } (1 .. $total_pages);
  144         368  
213 10 50       82 if (my $signature = $self->signature) {
214             # convert to the real signature
215 10 100       37 if ($signature == 1) {
216 2         5 $signature = $total_pages;
217             }
218 10 50       39 die "Should have already died, signature not a multiple of four" if $signature % 4;
219 10         27 my $half = $signature / 2;
220 10         179 my $offset = $self->paper_thickness_in_pt * ($half / 2);
221 10         459 my $original_offset = $self->paper_thickness_in_pt * ($half / 2);
222 10         88 my $signature_number = 0;
223 10         39 foreach my $page (1 .. $total_pages) {
224 144   66     413 my $page_in_sig = $page % $signature || $signature;
225 144 100       292 if ($page_in_sig == 1) {
226 14         25 $offset = $original_offset;
227 14         29 $signature_number++;
228             }
229 144         154 print "page in sig / $signature_number : $page_in_sig\n" if DEBUG;
230             # odd pages triggers a stepping
231 144 100       310 if ($page_in_sig % 2) {
232 72 100       220 if ($page_in_sig > ($half + 1)) {
    100          
233 22         449 $offset += $self->paper_thickness_in_pt;
234             }
235             elsif ($page_in_sig < $half) {
236 36         731 $offset -= $self->paper_thickness_in_pt;
237             }
238             }
239 144         671 my $rounded = $self->_round($offset);
240 144         184 print "offset for page is $rounded\n" if DEBUG;
241 144         605 $out{$page} = {
242             offset => $rounded,
243             signature => $signature_number,
244             signature_page => $page_in_sig,
245             };
246             }
247             }
248 10         220 return \%out;
249             }
250              
251             has total_input_pages => (is => 'lazy', isa => Int);
252              
253             sub _build_total_input_pages {
254 17     17   2778 my $self = shift;
255 17         333 my $count = $self->in_pdf_object->pages;
256 17         900 return $count;
257             }
258              
259             has total_output_pages => (is => 'lazy', isa => Int);
260              
261             sub _build_total_output_pages {
262 17     17   3307 my $self = shift;
263 17         329 my $total_input_pages = $self->total_input_pages;
264              
265 17 100       557 if (my $signature = $self->signature) {
266 13 100       77 if ($signature == 1) {
    100          
267             # all the pages on a single signature
268             # round to the next multiple of 4
269 3         6 my $missing = 0;
270 3 50       18 if (my $modulo = $total_input_pages % 4) {
271 0         0 $missing = 4 - $modulo;
272             }
273 3         71 return $total_input_pages + $missing;
274             }
275             elsif ($signature % 4) {
276 1         20 die "Signature must be 1 or a multiple of 4, but I got $signature";
277             }
278             else {
279 9         25 my $missing = 0;
280 9 100       46 if (my $modulo = $total_input_pages % $signature) {
281 6         13 $missing = $signature - $modulo;
282             }
283 9         214 return $total_input_pages + $missing;
284             }
285             }
286             else {
287 4         90 return $total_input_pages;
288             }
289             }
290              
291              
292             sub _measure_re {
293 61     61   330 return qr{([0-9]+(\.[0-9]+)?)\s*
294             (mm|in|pt|cm)}sxi;
295             }
296              
297             sub _string_to_pt {
298 44     44   102 my ($self, $string) = @_;
299             my %compute = (
300 24     24   131 mm => sub { $_[0] / (25.4 / 72) },
301 3     3   24 in => sub { $_[0] / (1 / 72) },
302 15     15   89 pt => sub { $_[0] / 1 },
303 2     2   59 cm => sub { $_[0] / (25.4 / 72) * 10 },
304 44         613 );
305 44         168 my $re = $self->_measure_re;
306 44 50       407 if ($string =~ $re) {
307 44         107 my $size = $1;
308 44         130 my $unit = lc($3);
309 44         139 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 15     15   2351 my $self = shift;
347 15         719 my $basename = fileparse($self->input, qr{\.pdf}i);
348 15         347 return $basename;
349             }
350              
351             sub _build_timestamp {
352 10     10   3026 my $now = localtime();
353 10         226 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 17     17   2136 my $self = shift;
364 17         375 my $name = File::Spec->catfile($self->_tmpdir, 'in.pdf');
365 17 50       386 copy ($self->input, $name) or die "Cannot copy input to $name $!";
366 17         13652 return $name;
367             }
368              
369             sub _build_out_pdf {
370 10     10   2867 my $self = shift;
371 10         283 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 17     17   3060 my $self = shift;
379 17         38 my $input = eval { PDF::API2->open($self->in_pdf) };
  17         363  
380 17 100 66     111784 if (!$input || $@) {
381 15         26 warn $@ if DEBUG && $@;
382             # same as in PDF::Imposition::Schema
383 15         3363 require CAM::PDF;
384 15         92385 my $src = CAM::PDF->new($self->in_pdf);
385 15         227203 my $tmpfile_copy = File::Spec->catfile($self->_tmpdir, 'v14.pdf');
386 15         301 $src->cleansave();
387 15         2087669 $src->output($tmpfile_copy);
388 15         43308 undef $src;
389 15         247 $input = PDF::API2->open($tmpfile_copy);
390             }
391 17 50       526989 if ($input) {
392 17         706 return $input;
393             }
394             else {
395 0 0       0 die "Cannot open " . $self->in_pdf unless $input;
396             }
397             }
398              
399             has out_pdf_object => (is => 'lazy', isa => Object);
400              
401             sub _build_out_pdf_object {
402 17     17   7570 my $self = shift;
403 17         132 my $pdf = PDF::API2->new;
404 17         13449 my $now = POSIX::strftime(q{%Y%m%d%H%M%S+00'00'}, localtime(time()));
405 17   66     473 $pdf->info(Creator => 'PDF::Cropmarks',
406             Producer => 'PDF::API2',
407             Title => $self->title || $self->basename,
408             CreationDate => $now,
409             ModDate => $now);
410 17         3256 $pdf->mediabox($self->_paper_dimensions);
411 17         2812 return $pdf;
412             }
413              
414             sub _paper_dimensions {
415 17     17   37 my $self = shift;
416 17         71 my $paper = $self->paper;
417 17         90 my %sizes = PDF::API2::Util::getPaperSizes();
418 17         1823 my $measure_re = $self->_measure_re;
419 17 100       199 if (my $dimensions = $sizes{lc($self->paper)}) {
    50          
420 15         194 return @$dimensions;
421             }
422             elsif ($paper =~ m/\A\s*
423             $measure_re
424             \s*:\s*
425             $measure_re
426             \s*\z/sxi) {
427             # 3 + 3 captures
428 2         8 my $xsize = $1;
429 2         6 my $xunit = $3;
430 2         8 my $ysize = $4;
431 2         7 my $yunit = $6;
432 2         9 return ($self->_string_to_pt($xsize . $xunit),
433             $self->_string_to_pt($ysize . $yunit));
434             }
435             else {
436 0         0 die "Cannot get dimensions from $paper, using A4";
437             }
438             }
439              
440             sub add_cropmarks {
441 10     10 1 11015 my $self = shift;
442 10 50       233 die "add_cropmarks already called!" if $self->_is_closed;
443 10         1562 my $needed = $self->total_output_pages - $self->total_input_pages;
444 10 50       544 die "Something is off, pages needed: $needed pages" if $needed < 0;
445 10         214 my @sequence = (1 .. $self->total_input_pages);
446 10 100       113 if ($needed) {
447 5         12 my $last;
448 5 100       33 if ($self->cover) {
449 2         6 $last = pop @sequence;
450             }
451 5         27 while ($needed > 0) {
452 16         29 push @sequence, undef;
453 16         38 $needed--;
454             }
455 5 100       20 if ($last) {
456 2         6 push @sequence, $last;
457             }
458             }
459 10         18 my $as_page_number = 0;
460 10         21 print Dumper(\@sequence) if DEBUG;
461 10         27 foreach my $src_page_number (@sequence) {
462 140         123948 $as_page_number++;
463             # and set it as page_number
464 140         605 $self->_import_page($src_page_number, $as_page_number);
465             }
466 10         9529 print "Saving " . $self->out_pdf . "\n" if DEBUG;
467 10         285 $self->out_pdf_object->saveas($self->out_pdf);
468 10         4507423 $self->in_pdf_object->end;
469 10         107666 $self->out_pdf_object->end;
470 10         181 print "Objects closed\n" if DEBUG;
471 10 50       237 move($self->out_pdf, $self->output)
472             or die "Cannot copy " . $self->out_pdf . ' to ' . $self->output;
473 10         2734 $self->_is_closed(1);
474 10         448 return $self->output;
475             }
476              
477             sub _round {
478 612     612   3870130 my ($self, $float) = @_;
479 612         783 print "Rounding $float\n" if DEBUG;
480 612 100       1777 return 0 unless $float;
481 580 100 100     2107 if ($float < 0.001 && $float > -0.001) {
482 28         54 return 0;
483             }
484 552         7627 return sprintf('%.3f', $float);
485             }
486              
487             has output_dimensions => (is => 'lazy', isa => ArrayRef);
488              
489             has title => (is => 'ro', isa => Maybe[Str]);
490              
491             sub _build_output_dimensions {
492 5     5   2579 my $self = shift;
493             # get the first page
494 5         125 my $in_page = $self->in_pdf_object->openpage(1);
495 5         242 return [ $in_page->get_mediabox ];
496             }
497              
498             sub _import_page {
499 140     140   313 my ($self, $src_page_number, $page_number) = @_;
500 140 100       3730 my $in_page = (defined $src_page_number ? $self->in_pdf_object->openpage($src_page_number) : undef);
501 140         337118 my $page = $self->out_pdf_object->page;
502 140         77682 my ($llx, $lly, $urx, $ury) = $page->get_mediabox;
503 140 50       9880 die "mediabox origins for output pdf should be zero" if $llx + $lly;
504 140         288 print "$llx, $lly, $urx, $ury\n" if DEBUG;
505 140 100       761 my ($inllx, $inlly, $inurx, $inury) = ($in_page ? $in_page->get_mediabox : @{$self->output_dimensions});
  16         374  
506 140         8311 print "$inllx, $inlly, $inurx, $inury\n" if DEBUG;
507 140 50       617 die "mediabox origins for input pdf should be zero" if $inllx + $inlly;
508             # place the content into page
509              
510 140         861 my $offset_x = $self->_round(($urx - $inurx) / 2);
511 140         587 my $offset_y = $self->_round(($ury - $inury) / 2);
512              
513             # adjust offset if bottom or top are missing. Both missing doesn't
514             # make much sense
515 140 50 66     1690 if (!$self->bottom && !$self->top) {
    100          
    100          
516             # warn "bottom and top are both false, centering\n";
517             }
518             elsif (!$self->bottom) {
519 28         74 $offset_y = 0;
520             }
521             elsif (!$self->top) {
522 24         79 $offset_y *= 2;
523             }
524              
525 140 50 66     1554 if (!$self->inner && !$self->outer) {
    100          
    100          
526             # warn "inner and outer are both false, centering\n";
527             }
528             elsif (!$self->inner) {
529 56 100 100     480 if ($self->twoside and !($page_number % 2)) {
530 22         63 $offset_x *= 2;
531             }
532             else {
533 34         79 $offset_x = 0;
534             }
535             }
536             elsif (!$self->outer) {
537 28 100 100     219 if ($self->twoside and !($page_number % 2)) {
538 8         21 $offset_x = 0;
539             }
540             else {
541 20         92 $offset_x *= 2;
542             }
543             }
544              
545 140         321 my $signature_mark = '';
546 140 100 100     1117 if ($self->signature && $self->twoside) {
547 92         2516 my $spec = $self->thickness_page_offsets->{$page_number};
548 92         1218 my $paper_thickness = $spec->{offset};
549 92 50       383 die "$page_number not defined in " . Dumper($self->thickness_page_offsets)
550             unless defined $paper_thickness;
551             # recto pages, increase
552 92 100       338 if ($page_number % 2) {
553 46         137 $offset_x += $paper_thickness;
554             }
555             # verso pages, decrease
556             else {
557 46         149 $offset_x -= $paper_thickness;
558             }
559 92         470 $signature_mark = ' #' . $spec->{signature} . '/' . $spec->{signature_page};
560             }
561              
562 140         265 print "Offsets are $offset_x, $offset_y\n" if DEBUG;
563 140 100       467 if ($in_page) {
564 124         2941 my $xo = $self->out_pdf_object->importPageIntoForm($self->in_pdf_object,
565             $src_page_number);
566 124         1478184 my $gfx = $page->gfx;
567 124         21097 $gfx->formimage($xo, $offset_x, $offset_y);
568             }
569 140         47595 if (DEBUG) {
570             my $line = $page->gfx;
571             $line->strokecolor('black');
572             $line->linewidth(0.5);
573             $line->rectxy($offset_x, $offset_y,
574             $offset_x + $inurx, $offset_y + $inury);
575             $line->stroke;
576             }
577 140         533 my $crop = $page->gfx;
578 140         21362 $crop->strokecolor('black');
579 140         18717 $crop->linewidth(0.5);
580 140         9690 my $crop_width = $self->cropmark_length_in_pt;
581 140         4433 my $crop_offset = $self->cropmark_offset_in_pt;
582             # left bottom corner
583 140         2282 $self->_draw_line($crop,
584             ($offset_x - $crop_offset, $offset_y),
585             ($offset_x - $crop_width - $crop_offset, $offset_y));
586              
587              
588 140         11520 $self->_draw_line($crop,
589             ($offset_x, $offset_y - $crop_offset),
590             ($offset_x, $offset_y - $crop_offset - $crop_width));
591              
592             # right bottom corner
593 140         11797 $self->_draw_line($crop,
594             ($offset_x + $inurx + $crop_offset, $offset_y),
595             ($offset_x + $inurx + $crop_offset + $crop_width,
596             $offset_y));
597 140         11433 $self->_draw_line($crop,
598             ($offset_x + $inurx,
599             $offset_y - $crop_offset),
600             ($offset_x + $inurx,
601             $offset_y - $crop_offset - $crop_width));
602              
603             # top right corner
604 140         11961 $self->_draw_line($crop,
605             ($offset_x + $inurx + $crop_offset,
606             $offset_y + $inury),
607             ($offset_x + $inurx + $crop_offset + $crop_width,
608             $offset_y + $inury));
609              
610 140         11691 $self->_draw_line($crop,
611             ($offset_x + $inurx,
612             $offset_y + $inury + $crop_offset),
613             ($offset_x + $inurx,
614             $offset_y + $inury + $crop_offset + $crop_width));
615              
616             # top left corner
617 140         11659 $self->_draw_line($crop,
618             ($offset_x, $offset_y + $inury + $crop_offset),
619             ($offset_x,
620             $offset_y + $inury + $crop_offset + $crop_width));
621              
622 140         11730 $self->_draw_line($crop,
623             ($offset_x - $crop_offset,
624             $offset_y + $inury),
625             ($offset_x - $crop_offset - $crop_width,
626             $offset_y + $inury));
627              
628             # and stroke
629 140         11768 $crop->stroke;
630              
631             # then add the text
632 140         5770 my $text = $page->text;
633 140         37781 my $marker = sprintf('Pg %.4d', $page_number);
634 140         4731 $text->font($self->out_pdf_object->corefont('Courier'),
635             $self->_round($self->font_size_in_pt));
636 140         34276 $text->fillcolor('black');
637              
638             # bottom left
639 140         20601 $text->translate($offset_x - (($crop_width + $crop_offset)),
640             $offset_y - (($crop_width + $crop_offset)));
641 140         70865 $text->text($marker);
642              
643             # bottom right
644 140         33130 $text->translate($inurx + $offset_x + $crop_offset,
645             $offset_y - (($crop_width + $crop_offset)));
646 140         61480 $text->text($marker);
647              
648             # top left
649 140         26818 $text->translate($offset_x - (($crop_width + $crop_offset)),
650             $offset_y + $inury + $crop_width);
651 140         60434 $text->text($marker);
652              
653             # top right
654 140         26496 $text->translate($inurx + $offset_x + $crop_offset,
655             $offset_y + $inury + $crop_width);
656 140         60670 $text->text($marker);
657              
658 140   66     30926 my $text_marker = ($self->title || $self->basename)
659             . ' ' . $self->timestamp .
660             ' page ' . $page_number . $signature_mark;
661             # and at the top and and the bottom add jobname + timestamp
662 140         5550 $text->translate(($inurx / 2) + $offset_x,
663             $offset_y + $inury + $crop_width);
664 140         61636 $text->text_center($text_marker);
665              
666 140         133962 $text->translate(($inurx / 2) + $offset_x,
667             $offset_y - ($crop_width + $crop_offset));
668 140         61160 $text->text_center($text_marker);
669             }
670              
671             sub _draw_line {
672 1120     1120   2266 my ($self, $gfx, $from_x, $from_y, $to_x, $to_y) = @_;
673 1120         3464 $gfx->move($from_x, $from_y);
674 1120         96000 $gfx->line($to_x, $to_y);
675 1120         90770 my $radius = 3;
676 1120         3453 $gfx->circle($to_x, $to_y, $radius);
677 1120         3268326 $gfx->move($to_x - $radius, $to_y);
678 1120         94833 $gfx->line($to_x + $radius, $to_y);
679 1120         92179 $gfx->move($to_x, $to_y - $radius);
680 1120         92946 $gfx->line($to_x, $to_y + $radius);
681             }
682              
683             sub DESTROY {
684 17     17   427459 my $self = shift;
685 17 100       504 unless ($self->_is_closed) {
686 7         935 $self->in_pdf_object->end;
687 7         32008 $self->out_pdf_object->end;
688             }
689             }
690              
691             =head1 AUTHOR
692              
693             Marco Pessotto, C<< >>
694              
695             =head1 BUGS
696              
697             Please report any bugs or feature requests to the CPAN's RT or at
698             L. If you find
699             a bug, please provide a minimal example file which reproduces the
700             problem.
701              
702             =head1 LICENSE
703              
704             This program is free software; you can redistribute it and/or modify
705             it under the terms of either: the GNU General Public License as
706             published by the Free Software Foundation; or the Artistic License.
707              
708             =cut
709              
710              
711             1;