]> git.sur5r.net Git - pdfstitch/blob - pdfstitch
BSD friendly shebang
[pdfstitch] / pdfstitch
1 #!/usr/bin/env perl
2
3 use 5.20.0;
4 use strict;
5 use utf8;
6 use warnings qw(all);
7
8 use File::Basename;
9 use File::LibMagic;
10 use Getopt::Long;
11 use PDF::API2;
12 use YAML;
13
14 my $help = '';
15 my $genmeta = '';
16 my $preview = '';
17 my $crop = '';
18 my $stitch = '';
19
20 Getopt::Long::Configure("bundling");
21
22
23 my $usage = <<ENDUSAGE;
24 pdfstitch - Copyright (C) 2017 by Jakob Haufe <sur5r\@sur5r.net>
25
26 Usage: $0 [-hgpcs] [--genmeta] [--preview] [--crop] [--stitch] {PDF file|.stitch file}
27
28  -h, --help     Display this message
29  -g, --genmeta  Generate .stitch file for stitching based on given PDF
30                 (default when called with a PDF)
31  -p, --preview  Generate preview PDF containing overlays to analyze
32                 cropping
33  -c, --crop     Generate cropped PDF according to given .stitch
34  -s, --stitch   Generate stitched PDF
35                 (default when called with a .stitch file)
36
37 pdfstitch is free software under the GNU AGPL version 3. See LICENSE for details.
38 ENDUSAGE
39
40 GetOptions
41 (
42     'h|help' => \$help,
43     'g|genmeta' => \$genmeta,
44     'p|preview' => \$preview,
45     'c|crop' => \$crop,
46     's|stitch' => \$stitch
47 ) or die "Call with --help to see available options.\n";
48
49 die $usage if $help;
50
51 die "--genmeta can not be combined with other actions!\n" if($genmeta and ($preview or $crop or $stitch));
52
53 die "No input file specified!\n" unless $ARGV[0];
54
55 my $infile = $ARGV[0];
56
57 die "$infile does not exist!\n" unless -e $infile;
58 die "$infile is not readable!\n" unless -r $infile;
59
60 if(not ($genmeta or $preview or $crop or $stitch))
61 {
62     my $magic = File::LibMagic->new();
63     my $mime_type;
64
65     if($magic->can('info_from_filename'))
66     {
67         $mime_type = $magic->info_from_filename($infile)->{mime_type}
68     }
69     else
70     {
71         # Fallback for File::Libmagic below 1.06
72         $mime_type = $magic->checktype_filename($infile);
73     }
74     if($mime_type =~ "^application/pdf")
75     {
76         print "Detected PDF, turning on --genmeta\n";
77         $genmeta = 1;
78     }
79     elsif ($mime_type =~  "^text/plain")
80     {
81         YAML::LoadFile($infile) or die "Failed to parse $infile as YAML!\n";
82         print "Detected YAML, turning on --stitch\n";
83         $stitch = 1;
84     }
85     else
86     {
87         die "$infile has unsupported type: $mime_type\n";
88     }
89 }
90
91 if($genmeta)
92 {
93     print "Generating meta file for " . basename($infile) . ".\n";
94     my $outfile = basename($infile) . ".stitch";
95
96     die "$outfile exists, aborting!\n" if -e $outfile;
97
98
99     my $pdf = PDF::API2->open($infile);
100
101     my $page = $pdf->openpage(1);
102     my ($llx, $lly, $urx, $ury) = $page->get_mediabox;
103
104     my $meta = {
105         input => basename($infile),
106         x => (($urx - $llx)*0.1)/2,
107         y => (($ury - $lly)*0.1)/2,
108         width => ($urx - $llx)*0.9,
109         height => ($ury - $lly)*0.9,
110         columns => int(sqrt($pdf->pages)),
111         rows => int(sqrt($pdf->pages)),
112         pageorder => [(1 .. $pdf->pages)],
113     };
114
115     foreach $page (1..$pdf->pages)
116     {
117         $meta->{pageoffsets}->{$page}->{x} = 0;
118         $meta->{pageoffsets}->{$page}->{y} = 0;
119     }
120
121     YAML::Bless($meta)->keys(['input','x','y','width','height','columns','rows', 'pageorder','pageoffsets']);
122     YAML::DumpFile($outfile,$meta);
123 }
124 else
125 {
126     my $meta = YAML::LoadFile($infile);
127     my $inpdf = PDF::API2->open($meta->{input});
128
129     if($preview or $crop)
130     {
131         my $previewpdf = PDF::API2->new() if $preview;
132         my $croppedpdf = PDF::API2->new() if $crop;
133         my $transparency;
134
135         if($preview)
136         {
137             $transparency = $previewpdf->egstate();
138             $transparency->transparency(0.8);
139         }
140
141         foreach my $pagenr (@{$meta->{pageorder}})
142         {
143             next if $pagenr eq "blank";
144
145             my $llx = $meta->{x} + $meta->{pageoffsets}->{$pagenr}->{x};
146             my $lly = $meta->{y} + $meta->{pageoffsets}->{$pagenr}->{y};
147             my $urx = $meta->{width};
148             my $ury = $meta->{height};
149
150             if($preview)
151             {
152                 my $previewpage = $previewpdf->import_page($inpdf, $pagenr, 0);
153                 my $previewcontent = $previewpage->gfx();
154                 $previewcontent->egstate($transparency);
155                 $previewcontent->rect($llx, $lly, $urx, $ury);
156                 $previewcontent->fillcolor('%F000');
157                 $previewcontent->fill();
158             }
159             if($crop)
160             {
161                 my $croppage = $croppedpdf->import_page($inpdf, $pagenr, 0);
162                 $croppage->cropbox($llx, $lly, $llx + $urx, $lly + $ury);
163             }
164         }
165
166         $previewpdf->saveas(basename($infile, ('.pdf.stitch', '.stitch')) . '-preview.pdf') if $preview;
167         $croppedpdf->saveas(basename($infile, ('.pdf.stitch', '.stitch')) . '-cropped.pdf') if $crop;
168     }
169
170     if($stitch)
171     {
172         my $width = $meta->{width} * $meta->{columns};
173         my $height = $meta->{height} * $meta->{rows};
174
175         my $stitchedpdf = PDF::API2->new();
176
177         my $page = $stitchedpdf->page();
178         $page->mediabox($width + 100, $height + 100);
179
180         my $content = $page->gfx();
181         my $column = 1;
182         my $row = 1;
183
184         foreach my $pagenr (@{$meta->{pageorder}})
185         {
186             if($pagenr ne "blank")
187             {
188                 my $xo = $stitchedpdf->importPageIntoForm($inpdf, $pagenr);
189
190                 my $llx = $meta->{x} + $meta->{pageoffsets}->{$pagenr}->{x};
191                 my $lly = $meta->{y} + $meta->{pageoffsets}->{$pagenr}->{y};
192                 my $urx = $llx + $meta->{width};
193                 my $ury = $lly + $meta->{height};
194
195                 $xo->bbox($llx, $lly, $urx, $ury);
196
197                 my $xpos = ($column - 1) * $meta->{width};
198                 my $ypos = $height - ($row * $meta->{height});
199                 $content->formimage($xo, $xpos, $ypos);
200             }
201             $column++;
202             if($column > $meta->{columns})
203             {
204                 $row++;
205                 $column=1;
206             }
207         }
208
209         $stitchedpdf->saveas(basename($infile, ('.pdf.stitch','.stitch')) . '-stitched.pdf');
210     }
211 }
212