[pdftex] update to pdfbook utility

Jaap Eldering eldering at phys.uu.nl
Tue Jul 26 11:52:34 CEST 2005


Hi all,

I recently was searching for a pdf utility to convert documents to a
book format like psbook+psnup+pstops and found a previous post to this
list: http://www.tug.org/pipermail/pdftex/2004-February/004760.html

I rewrote parts of this small program, because it didn't completely
fit my needs (although a good starting point).

Changes to the original include:
- using layout "nup=1x2" is optional.
- included option to pass parameters directly to pdfpages command,
  which generates the new document: makes it easier to e.g. also make
  a booklet from A4 to A6 (2 copies) using:
      pdfbook -o 'pages=-,doublepages=true,nup=2x2,column=true'
- generate everything in a temporary directory to avoid filename
  conflicts, race conditions, etc.
- fixed bug when filenames contained more than one `.'.
- the option to output pages in reverse order was lost in the process,
  as I have no experience with that (I'm not Hebrew ;-).

Might be useful for others...

Kind regards,
Jaap Eldering

===============================================

/*
 * $Id: pdfbook.c 1507 2005-06-16 21:23:50Z root $
 *
 * pdfbook.c    Rearrange pages in a PDF file into signatures.
 *
 *
 * Original author     Tigran Aivazian <tigran at aivazian.fsnet.co.uk>
 * Modifications by    Jaap Eldering <eldering at a-eskwadraat.nl>
 *
 * Based on the algorithm from psutils/psbook.c, which was
 * written by Angus J. C. Duggan 1991-1995.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>

#define TMP_INFILE_BASE  "input"
#define TMP_OUTFILE_BASE "output"

char *program;

static void usage(void)
{
	fprintf(stderr, "Usage: %s [-q] [-2] [-s <signature>] infile outfile\n"
	                "  or:  %s [-q] -o <options>          infile outfile\n"
	                "Rearrange pages for printing as booklet.\n\n"
	                "Options:\n"
	                "  -q  suppress verbose output\n"
	                "  -2  place 2 pages on 1 page of output\n"
	                "  -s  group pages together in groups of size <signature>\n"
	                "      <signature> must be positive and divisible by 4\n"
	                "  -o  pass <options> directly to LaTeX pdfpages `includepdf' command\n"
	                "      see the pdfpages package documentation for possible options\n",
			program, program);
	fflush(stderr);
	exit(1);
}

char *make_tempdir()
{
	static char dirtemplate[L_tmpnam+10];
	char *dirname;
	
	if ( (dirname = tempnam(NULL, "pdfbk")) == NULL ) {
		fprintf(stderr, "%s: error generating temporary directory\n", program);
		exit(1);
	}
	strcpy(dirtemplate, dirname);
	strcat(dirtemplate, "XXXXXX");

	if ( (dirname = mkdtemp(dirtemplate)) == NULL ) {
		fprintf(stderr, "%s: error generating temporary directory\n", program);
		exit(1);
	}
	
	return dirname;
}

char *allocstr(char *format, ...)
{
        va_list ap;
        char *str;
        char tmp[2];
        int len, n;

        va_start(ap,format);
        len = vsnprintf(tmp,1,format,ap);
        va_end(ap);

        if ( (str = (char *) malloc(len+1))==NULL ) return NULL;

        va_start(ap,format);
        n = vsnprintf(str,len+1,format,ap);
        va_end(ap);

        if ( n==-1 || n>len ) return NULL;

        return str;
}

int main(int argc, char *argv[])
{
	char *infile  = NULL;
	char *outfile = NULL;
	char *tmpdir;
	char *tmptexfile;
	char *tmpinfile;
	char *tmpoutfile;

	FILE *fp, *fout;
	
	int *actualpg;
	
	static char cmdline[1024];
	
	int quiet = 0;
	int nup = 0;
	int pdfcustom = 0;
	int signature = 0;
	int npages = 0;
	int maxpage;

	char *pdfcustom_str = NULL;
	
	int i, c;
	
	program = argv[0];

	tmpdir = make_tempdir();

	while ( (c = getopt(argc, argv, "s:2o:q")) != -1 ) {
		switch (c) {
		case 's':
			signature = atoi(optarg);
			if (signature < 1 || signature % 4)
				usage();
			break;
			
		case '2':
			nup = 1;
			break;
			
		case 'o':
			pdfcustom = 1;
			if ( (pdfcustom_str = strdup(optarg)) == NULL ) {
				fprintf(stderr, "%s: error allocating memory\n", program);
				exit(1);
			}
			break;
			
		case 'q':
			quiet = 1;
			break;
			
		default:
			usage();
		}
	}

	if ( optind<argc ) {
		infile = argv[optind++];
	} else {
		fprintf(stderr, "%s: input file must be specified\n", program);
		usage();
	}
			
	if ( optind<argc ) {
		outfile = argv[optind++];
	} else {
		fprintf(stderr, "%s: output file must be specified\n", program);
		usage();
	}

	if ( pdfcustom && (nup || signature) ) {
		fprintf(stderr, "%s: option -o cannot be combined with other options\n", program);
		usage();
	}
	
	sprintf(cmdline, "pdfinfo %s | sed -ne \"s/Pages: *\\([0-9]*\\)/\\1/p\"", infile);
	fp = popen(cmdline, "r");
	if (fscanf(fp, "%d", &npages) != 1) {
		fprintf(stderr, "%s: error reading \"%s\"\n", program, infile);
		exit(1);
	}
	if (npages < 1) {
		fprintf(stderr, "%s: invalid number of pages=%d\n", program, npages);
		exit(1);
	}

	if ( (tmptexfile = allocstr("%s/%s.tex",tmpdir,TMP_OUTFILE_BASE)) == NULL ) {
		fprintf(stderr, "%s: error allocating memory\n", program);
		exit(1);
	}
	
	if ( (tmpinfile  = allocstr("%s/%s.pdf",tmpdir,TMP_INFILE_BASE)) == NULL ) {
		fprintf(stderr, "%s: error allocating memory\n", program);
		exit(1);
	}
	
	if ( (tmpoutfile = allocstr("%s/%s.pdf",tmpdir,TMP_OUTFILE_BASE)) == NULL ) {
		fprintf(stderr, "%s: error allocating memory\n", program);
		exit(1);
	}
		
	fout = fopen(tmptexfile, "w");
	if (!fout) {
		fprintf(stderr, "%s: error opening \"%s\" for write\n",
			program, tmptexfile);
		exit(1);
	}

	if (!signature)
		signature = maxpage = npages + (4 - npages%4)%4;
	else
		maxpage = npages + (signature - npages%signature)%signature;
	
	if ( (actualpg = (int *) calloc(maxpage,sizeof(int))) == NULL ) {
		fprintf(stderr, "%s: error allocating memory\n", program);
		exit(1);
	}
	
	for (i=0; i<maxpage; i++) {
		int actual = i - i%signature;
		
		switch (i%4) {
		case 0:
		case 3:
			actual += signature - 1 - (i%signature)/2;
			break;
		case 1:
		case 2:
			actual += (i%signature)/2;
			break;
		}
		if (actual < npages)
			actualpg[i] = actual + 1;
	}

	fprintf(fout,
		"\\documentclass{book}\n"
		"\\usepackage{pdfpages}\n"
		"\\begin{document}\n");
	
	if ( pdfcustom ) {
		fprintf(fout, "\\includepdf[%s]", pdfcustom_str);
	} else {
		if ( nup ) {
			fprintf(fout,"\\includepdf[pages=-,signature=%d , landscape]",signature);
		} else {
			fprintf(fout,"\\includepdf[pages={");
			
			for (i=0; i<maxpage; i++) {
				if (actualpg[i]) {
					fprintf(fout, "%d", actualpg[i]);
				} else {
					fprintf(fout, "{}");
				}
				fprintf(fout, "%s", i<maxpage-1 ? "," : "");
			}
			fprintf(fout,"}]");
		}
	}
	
	fprintf(fout,"{%s.pdf}\n\\end{document}\n", TMP_INFILE_BASE);
		
	fclose(fout);
	
	if (!quiet)
		printf("%s: Generating output file now, please wait...\n", program);
	
	sprintf(cmdline, "cp %s %s", infile, tmpinfile);
	if (system(cmdline)) {
		fprintf(stderr, "%s: Failed to copy \"%s\" file to \"%s\"\n",
			program, tmpoutfile, tmpinfile);
		exit(1);
	}
	sprintf(cmdline, "cd %s && pdflatex %s > /dev/null 2>&1 < /dev/null",
		tmpdir, tmptexfile);
	if (system(cmdline)) {
		fprintf(stderr, "%s: Failed to generate output, see \"%s/%s.log\" for details\n",
			program, tmpdir, TMP_OUTFILE_BASE);
		exit(1);
	}
	sprintf(cmdline, "cp %s %s", tmpoutfile, outfile);
	if (system(cmdline)) {
		fprintf(stderr, "%s: Failed to write \"%s\" file\n",
			program, outfile);
		exit(1);
	}
	
	sprintf(cmdline, "rm -rf %s", tmpdir);
	system(cmdline);
	
	return 0;
}



More information about the pdftex mailing list