#!/usr/bin/perl -s use File::Spec; use Config; our ($q,$o,$debug,$a3,$a5,$a6,$ag,$ishtml,$flyer, $scale,$pages,$offset,$nobook,$even,$odd); use strict; use warnings; my $usage=q{ psmallbook f.tex psmallbook f.dvi psmallbook f.pdf psmallbook f1.pdf f2.pdf ... fn.pdf psmallbook http://...../f.html options -o=f.pdf send output to f.pdf (by default _out.pdf) -np=20 set the number of pages -ppu=16 defines the number of pages by book unit -a3 output in 2 pages per A3 page -a5 output in 2 pages per A4 page (default) -a6 output in 4 pages per A4 page (a5 by default) -ag output in 2 vertical pages per page (agenda 10.5x22cm) -flyer single booklet with 6 pages / 3pages in front,3 pages back) -q make latex work in batch mode -small already small; don't scale it... -pages=4-8 pages range to select from input -ishtml force html input -istxt force txt input (#not yet) -isa42up input is A4 2UP pages (#not yet) -scale=2.1 scale factor -offset=3cm,1cm move pages -even=3cm,1cm // -odd=-3cm,1cm move pages -nobook just scale,offset or select pages, (no booklets) }; die ("usage: $usage\n") unless @ARGV; my $prefixo = "_smallbook$$"; my $tmp = 0; my $latopt = ""; my $paper = "a4paper"; my $htmlop=""; if($a5){$htmlop .= "--fontsize 12pt "; } if($ag){$htmlop .= "--header '' --footer ' C ' --headfootsize 10.0 --left 0.9cm --size 10.7x21.9cm --fontsize 9.0 --textfont times "; } if($a6){$htmlop .= "--left 1.5cm --fontsize 14pt "; } if($q) {$latopt = " --interaction batchmode " } if($a3){$paper="a3paper" } my $texstart=q( %\documentclass[portuges,a4paper]{book} \documentclass{book} \RequirePackage[). $paper . q(]{geometry} %\usepackage{aeguill} \usepackage{pdfpages} \usepackage{t1enc} \begin{document} ); my $texend= q( \end{document} ); my $out; if($flyer){ $out = flyer(pdfcatargs(@ARGV)); } else { $out = pdf2book(pdfcatargs(@ARGV)); if ($a6) { $out = pdf2book($out) } } if ($o) { rename ($out, $o)} else { rename ($out, "_out.pdf")} clean(); sub flyer{ my $f=shift; my $t=nname(); my $ps= pages($f); my ($p1,$p2); if ($ps==3){$p1="1,2,3";$p2=""} elsif($ps==4){$p1="2,3,4";$p2="1,{},{}"} elsif($ps==5){$p1="2,3,4";$p2="1,{},5"} else {$p1="2,3,4";$p2="1,6,5"} open(F,">$t.tex") or die; print F $texstart; print F q( \includepdf[pagecommand={\thispagestyle{empty} \mbox{} \vskip 5.7cm ---\hfill--- \vskip 9.4cm ---\hfill---}, noautoscale,pages={), $p1, q(},nup=1x3,landscape]{),$f,q(} \includepdf[noautoscale,angle=180,pages={), $p2 ,q(},nup=1x3,landscape]{),$f,q(} ); print F $texend; close F; executa("pdflatex $latopt $t"); "$t.pdf"; } sub _check_prog { my $ac_prog = shift; my $PATH = $ENV{PATH}; my $p; for $p (split /$Config{path_sep}/,$PATH) { my $cmd = File::Spec->catfile($p,$ac_prog); return $cmd if -x $cmd; } return undef; } sub clean{ for (<_smallbook*>){ unlink($_) unless $debug; } } sub pdfcatargs{ my @A=@_; if(scalar @A == 1){ return topdf($A[0]);} my $t= nname(); open(F,">$t.tex") or die; print F $texstart; for (@A){ print F q( \includepdf[fitpaper=false,pages=-]{).topdf($_).q(}); } print F $texend; close F; executa("pdflatex $latopt $t"); "$t.pdf"; } sub topdf { my $f=shift; my $f2=topdf1($f); if ($pages || $scale || $offset || $even || $odd){ ## -offset=3cm,1cm $offset and $offset =~ s/,/ /g; $even and $even =~ s/,/ /g; $odd and $odd =~ s/,/ /g; my $t=nname(); my $pg= ($pages ? ",pages={$pages}" : ",pages=-"); my $sc= ($scale ? ",scale=$scale" : ""); if ($even || $odd) { ## NOT WORKING WITH $pages YET my $p=pages($f); open F,">$t.tex" or die; print F $texstart; for (1..$p) { $pg = ",pages=$_"; my $ofs = ($_%2)?",offset=$odd":",offset=$even"; print F q(\includepdf[noautoscale), $pg , $sc, $ofs , q(]{) , $f2 , qq(}\n); } print F $texend; close F; } else { my $ofs= ($offset ? ",offset=$offset" : ""); open(F,">$t.tex") or die; print F $texstart; print F q(\includepdf[noautoscale), $pg , $sc, $ofs , q(]{) , $f2 , qq(}\n); print F $texend; close F; } executa("pdflatex $latopt $t"); return "$t.pdf"; } else { return $f2; } } sub topdf1{ my $f=shift; if($f =~ m!(.*)\.pdf$!){ return "$f"; } elsif($f =~ m!(.*)\.html?$! or $ishtml){ my $t= nname(). ".pdf"; system_check("htmldoc"); if ($ENV{http_proxy} && $f=~/^http/) { $htmlop.=" --proxy $ENV{http_proxy} "; } executa("htmldoc --size a4 --duplex $htmlop -f $t --webpage '$f'"); return $t; } elsif($f =~ m!(.*)\.ps$! ){ my $t= nname(). ".pdf"; system_check("ps2pdf"); executa("ps2pdf $f $t"); return $t; } elsif($f =~ m!(.*)\.dvi$!){ my $base=$1; executa("dvipdf -sPAPERSIZE=a4 $base"); return "$base.pdf"; } elsif($f =~ m!(.*)\.tex$!){ my $base=$1; executa("pdflatex $latopt $base"); return "$base.pdf"; } } sub pages{ my $command = _check_prog("pdfinfo"); $command = _check_prog("pdfinfo.pl") unless $command; die ("Can't find pdfinfo or pdfinfo.pl. Please install PDF::CAM\n") unless $command; my $p=`$command "$_[0]" | grep -i pages`; ## get the number of pages of $f my $NP=$p; $NP=~ s/.*?(\d+).*$/$1/s; $NP } sub pdf2book{ my $f=shift; return $f if $nobook; my $p=pages($f); my $NP=(int(($p-1)/4) + 1 ) * 4; my $t= nname(); open(F,">$t.tex") or die; print F $texstart; if($ag){ print F sprintf('\includepdf[noautoscale,nup=2x1,pages={%s}]{%s}', signature($p), $f) } else { print F sprintf('\includepdf[pages=-,signature=%d,landscape]{%s}', $NP, $f) } print F "$texend"; close F; executa("pdflatex $latopt $t"); "$t.pdf"; } sub executa { my $cmd = shift; print STDERR "$cmd\n" if $debug; system ($cmd) == 0 or warn "** ERROR ************ system $cmd failed: $!$?\n"; } sub nname{ "$prefixo-". ($tmp++) } # newname... sub system_check { my $g="Get if from http:/"; my %known_programs = ( 'pdftops' => "pdftops, part of Xpdf. $g/www.foolabs.com/xpdf", 'a2ps' => "a2ps. $g/www.inf.enst.fr/~demaille/a2ps/", 'htmldoc' => "doc. $g/www.htmldoc.org/", 'pstops' => "psutils. $g/www.tardis.ed.ac.uk/~ajcd/psutils/", 'psbook' => "psutils. $g/www.tardis.ed.ac.uk/~ajcd/psutils/", 'psnup' => "psutils. $g/www.tardis.ed.ac.uk/~ajcd/psutils/", ); while (my $program = shift) { my $which = `which $program 2>&1`; if ($which =~ /^which:/) { die ("I need " . ($known_programs{$program} || $program)); } } } sub signature{ # number of pages my $np=shift; my $nc=(int(($np-1)/4) + 1 ); my $npv=$nc*4; my @p=(); join(",", map { (_e($npv-$_*2,$np), _e($_*2+1,$np), _e($_*2+2,$np) , _e($npv-$_*2-1,$np))} (0..$nc-1)); } sub _e{ ($_[0] <= $_[1])? $_[0] : "{}"; } sub a6signature{ # number of pages, $ppu my ($np,$ppu)=@_; my $nc = $ppu/8; my $delta = ($ppu - $np >= 4 )? 2:0; my @fmt = map {(mkcad($_*2-1, ($nc+$_)*2-1)) } reverse(1..$nc); @fmt = map {$q = $_ - $delta; ($q < 0 )? $ppu + $q : $q } @fmt ; "$ppu:". join("," , @fmt); } sub mkcad { my ($a,$b)=@_; ($a ,-$a-1, $b, -$b-1, -$a, $a-1, -$b , $b-1) } __END__ if ($t eq "txt") { system_check("a2ps"); executa("a2ps --border 0 --pretty-print=plain -M A4 -o $psfile -1 $filename"); } if ($t eq "html") { system_check("htmldoc"); if ($ENV{http_proxy} && $filename=~/^http/) { $htmlop.=" --proxy $ENV{http_proxy} "; } executa("htmldoc --size a4 --duplex $htmlop -f $psfile --webpage '$filename'") } ## get the number of pages if you dont know it... ($np) = $np || (`grep '%%Pages' $psfile` =~ m/\%\%Pages:\s*(\d+)/g ); ## ppu - number of pages per unit... (by default = next mult. of 8 after np) if ($ppu) { $si="-s$ppu";} else { $si = "" ; $ppu = (int(($np-1)/8) + 1 ) * 8 ;} # only print a range of pages (see option 'pages' of psselect for details) if ($pages) {$pr = "psselect $pages $psfile |"; $psfile=""} else {$pr = ""} my $Uodd = "$op '2:0,1U(1w,1h)'"; # turn odd pages upsidedown for duplex print. if($a3){ system_check("pstops", "psbook", "psnup"); executa("cat $psfile|$pr $scaleit psbook $si | psnup $op -Pa3 -2 | pstops -pa3 $Uodd >$w"); } if($a5){ system_check("pstops", "psbook", "psnup"); executa("cat $psfile|$pr $scaleit psbook $si | psnup $op -Pa4 -2 | pstops $Uodd >$w") } if($ag){ system_check("pstops", "psbook", "psnup"); executa("cat $psfile|$pr $scaleit psbook $si | psnup $op -W10.5cm -H29cm -2 >$w") } if($a6){ system_check("pstops", "psbook", "psnup"); executa("cat $psfile|$pr $scaleit pstops -pa4 " . a6duplex() . " | psnup $op -4 >$w") } if($flyer){ system_check("pstops"); executa("cat $psfile|$pr pstops '6:1L(21cm,0)+2L(21cm,9.9cm)+3L(21cm,19.8cm),0R(0cm,9.90cm)+5R(0cm,19.8cm)+4R(0,29.7cm)' > $w"); } sub a6duplex{ # uses $ppu and $np my $nc = $ppu/8; my $delta = ($ppu - $np >= 4 )? 2:0; my @fmt = map {(mkcad($_*2-1, ($nc+$_)*2-1)) } reverse(1..$nc); @fmt = map {$q = $_ - $delta; ($q < 0 )? $ppu + $q : $q } @fmt ; "$ppu:". join("," , @fmt);} sub mkcad { my ($a,$b)=@_; ($a ,-$a-1, $b, -$b-1, -$a, $a-1, -$b , $b-1) } =head1 NAME psmallbook - a perl script to make a duplex book in A5, A6 or agenda format =head1 SYNOPSIS psmallbook f.tex psmallbook f.dvi psmallbook f.pdf psmallbook f1.pdf f2.pdf ... fn.pdf psmallbook http://...../f.html psmallbook -nobook -offset=1cm,1cm f.pdf options -o=f.pdf send output to f.pdf (by default _out.pdf) * -np=20 set the number of pages * -ppu=16 defines the number of pages by book unit -a3 output in 2 pages per A3 page -a5 output in 2 pages per A4 page (default) -a6 output in 4 pages per A4 page (a5 by default) -ag output in 2 vertical pages per page (agenda 10.5x22cm) -ag output in 2 vertical pages per page (agenda 10.5x22cm) -flyer single booklet with 6 pages ( 3pages in front,3 pages back) -q make latex work in batch mode * -small already small; don't scale it... -pages=4-8 pages range to select from input -ishtml force html input * -istxt force txt input (#not yet) * -isa42up input is A4 2UP pages (#not yet) -scale=2.1 scale factor -offset=3cm,1cm move pages -even=3cm,1cm // -odd=-3cm,1cm move pages -nobook just scale,offset or select pages, (no booklets) =head1 DESCRIPTION This command was created to make small domestic books... This script takes a LaTeX, DVI, PDF or PDF file and makes multipage per page PDF file forming a smallbook ready to print (if possible in a duplex printer) and cut. Available options permits to select : =over =item * a 4 pages/page A6 book (-a6) (seealso a6.sty in the distribution directory) =item * a 2 pages/page A5 book ( default) (seealso a5.sty and b5.sty) =item * a 2 pages/page agenda (10.5 x 22cm each page) (-ag) (seealso agbook.sty) =back You need to install LaTeX and pdfpages.sty If you use a5.sty, b5.sty, a6.sty or agbook.sty you need the package geometry.sty installed (it comes with some distributions but if you get many errors upgrade geometry from CTAN or do make updategeometry To make agbook: C<\usepackage{agbook}> in the LaTeX source. We provide a set of LaTeX formats (C<\usepackage{a5}> or C<\usepackage{a6}>) to make the characters look readable. They make a4 pages, and psmallbook scales them. If you want, You can use any format that makes a4 pages (LateX seminar makes good a6 books). =head1 HTML Support Using Doc it is possible do download pages directly from the Internet. If you need proxy support, export the C variable export http_proxy=http://proxy.from.server.com:8080 =head1 Input en A4 2 Up pages Use the C<-isa42up> option. Internally, first is built a normal A4 postscript file and then it is piped to psmallbook. =head1 Some tips... If you want do make a A5 booklet from a LaTeX file, it is usefull to scale it. Try this (and change the move-parameters). psmallbook -scale=1.19 -offset=-3cm,-2.5cm in.pdf =head1 REQUIREMENTS To use html and URL's, you need EhtmlEdoc. Get it from http://www.easysw.com/htmldoc/", =head1 SEE ALSO psutils pdftops perl(1) LaTeX(1) (pdflatex) CTAN psbook(1) a6.sty a5.sty b5.sty flyer.sty cdbook.sty agbook.sty a2ps =head1 AUTHOR José João Almeida (jj@di.uminho.pt) Natura project Source available in http://natura.di.uminho.pt/~jj/text/psmallbook =cut \includepdf[pagecommand={\thispagestyle{empty}},