1#! /bin/sh
2eval '(exit $?0)' && eval 'PERL_BADLANG=x;PATH="$PATH:.";export PERL_BADLANG\
3 PATH;exec perl -x -S -- "$0" ${1+"$@"};#'if 0;eval 'setenv PERL_BADLANG x\
4;setenv PATH "$PATH":.;exec perl -x -S -- "$0" $argv:q;#'.q
5#!perl -w
6+push@INC,'.';$0=~/(.*)/s;do(index($1,"/")<0?"./$1":$1);die$@if$@__END__+if 0
7;#Don't touch/remove lines 1--7: http://www.inf.bme.hu/~pts/Magic.Perl.Header
8#
9# sam2p_pdf_scale.pl: scale PDF created by sam2p to page size
10# by pts@fazekas.hu at Sat Jun  6 17:16:17 CEST 2009
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20# GNU General Public License for more details.
21#
22# TODO(pts): Rotate the image by 90 degrees if necessary.
23
24use integer;
25use strict;
26die "Usage: $0 <page-width-bp> <page-height-bp> <input.pdf> [<output.pdf>]\n".
27    "This program reads a PDF generated by sam2p, ".
28    "and scales it to page size.\n" if @ARGV != 3 and @ARGV != 4;
29die "page-width not a nonnegative number\n" if $ARGV[0] !~ m@\A[\d.]+\Z(?!\n)@;
30die "page-height not a nonnegative number\n"
31    if $ARGV[1] !~ m@\A[\d.]+\Z(?!\n)@;
32sub float2str($) {
33  no integer;
34  my $F = $_[0] + 0;
35  $F = "$F";
36  if (index($F, 'e') >= 0) {
37    $F = sprintf("%.20f", $F);
38    $F =~ s@[.]?0+\Z@@;
39  }
40  $F
41}
42#** Desired page size dimensions. Must be integers, measured in bp (inch/72).
43my $dwidth = float2str($ARGV[0]);
44my $dheight = float2str($ARGV[1]);
45my $outfn = @ARGV == 4 ? $ARGV[3] : $ARGV[2];
46my $F;
47die "cannot open input PDF: $ARGV[2]: $!\n" if !open($F, '<', $ARGV[2]);
48my $S = join('', <$F>);
49die if !close($F);
50die "sam2p PDF syntax error (no cm)\n" if $S!~m@\n((\d+) 0 0 (\d+) 0 0 cm\b)@g;
51my $cm_at = pos($S) - length($1);
52my $cm_size = length($1);
53my $iwidth = $2 + 0;
54my $iheight = $3 + 0;
55die "sam2p PDF image error (empty image)\n" if $iwidth < 0 or $iheight < 0;
56my $rest_at = rindex($S, "\nendstream\n") + 1;
57die "sam2p PDF syntax error (no endstream)\n" if $rest_at <= $cm_at;
58pos($S) = $rest_at;
59die "sam2p PDF syntax error (boxes not found)\n" if
60    $S!~m@(/MediaBox\s*\[0 0 (\d+) (\d+)\]\s*/CropBox\s*\[0 0 (\d+) (\d+)\])@g;
61my $boxes_at = pos($S) - length($1);
62my $boxes_size = length($1);
63die "sam2p PDF syntax error (image size mismatch)\n" if
64    $2 ne $iwidth or $3 ne $iheight or
65    $4 ne $iwidth or $5 ne $iheight;
66my $boxes_new =
67    "/MediaBox[0 0 $dwidth $dheight]\n/CropBox[0 0 $dwidth $dheight]";
68substr($S, $boxes_at, $boxes_size) = $boxes_new;
69my($cwidth, $cheight);
70my($xshift, $yshift);
71{ no integer;
72  my $aheight = $iheight * $dwidth / $iwidth;
73  my $bwidth  = $iwidth * $dheight / $iheight;
74  if ($aheight <= $dheight) { ($cwidth, $cheight) = ($dwidth, $aheight) }
75  else { ($cwidth, $cheight) = ($bwidth, $dheight) }
76  $xshift = ($dwidth - $cwidth) / 2;
77  $yshift = ($dheight - $cheight) / 2;
78}
79my $cm_new = float2str($cwidth)." 0 0 ".float2str($cheight)." ".
80    float2str($xshift)." ".float2str($yshift)." cm";
81substr($S, $cm_at, $cm_size) = $cm_new;
82pos($S) = 0;
83if ($S =~ m@\n<</Length ((\d+)>>\nstream\nq\n)@g and pos($S) == $cm_at) {
84  substr($S, pos($S) - length($1), length($2)) =
85      sprintf("%".length($2)."d", $2 + length($cm_new) - $cm_size);
86}
87pos($S) = $boxes_at + length($boxes_new) + length($cm_new) - $cm_size;
88die "sam2p PDF syntax error (xref not found)\n" if
89    $S!~m@\sendobj\n(xref\n0 (\d+)\n0000000000 65535 f \n)@g;
90my $xref_at = pos($S) - length($1);
91my $obj_count = $2 - 1;
92my $ofss_at = pos($S);
93for (my $I = 1; $I <= $obj_count; ++$I) {
94  my $xrefe_at = $ofss_at + ($I - 1) * 20;
95  my $xrefe = substr($S, $xrefe_at, 20);
96  die "sam2p PDF syntax error (xref entry $I bad)\n" if
97      $xrefe!~m@\A(\d{10}) 00000 n \n\Z(?!\n)@;
98  my $ofs = $1 + 0;
99  $ofs += (
100     ($ofs >= $boxes_at) ? length($boxes_new) - $boxes_size +
101                           length($cm_new) - $cm_size :
102     ($ofs >= $cm_at) ?    length($cm_new) - $cm_size : 0);
103  substr($S, $xrefe_at, 20) = sprintf("%010d 00000 n \n", $ofs);
104}
105pos($S) = $ofss_at + $obj_count * 20;
106die "sam2p PDF syntax error (startxref not found)\n" if
107    $S!~s@\nstartxref\n(\d+)\n@\nstartxref\n$xref_at\n@;
108die "cannot open output PDF: $outfn: $!\n" if !open($F, '>', $outfn);
109die if !print($F $S);
110die if !close($F);
111