[pdftex] pdfbook.c utility.

Tigran Aivazian tigran at aivazian.fsnet.co.uk
Fri Feb 13 00:17:58 CET 2004


Hi,

I was using dvips+psbook+pstops combination in the days of LaTeX, but now 
having switched to pdfLaTeX completely I couldn't find a way to re-arrange 
the pages in a PDF file into signatures, i.e. for printing books as 
printed sheets (like it is done in real book production), i.e. the 
equivalent of the following two scripts:

# for Greek (European, left-edge) binding
dvips -q -j0 -x707 -t a4 -t landscape gnt.dvi -f | \
psbook -q -s32 | \
pstops -q -pa4 '2:0(7.44mm,7.44mm)+1(7.44mm,-141.06mm)'> gnt-book.ps


# for Hebrew (right-edge) binding
dvips -q -j0 -x707 -t a4 -t landscape tnk.dvi -f | \
psbook -q -s32 | \
pstops -q -pa4 '2:1(7.44mm,7.44mm)+0(7.44mm,-141.06mm)'> tnk-book.ps

Even with pdfpages package, one still has to specify the sequence of pages 
explicitly, so I have written a pdfbook.c utility which uses the same
syntax as psbook(1) and does:

a) generate the page sequence for a given signature number (it obtains
the total number of pages directly from the input PDF file)

b) construct the .tex file which uses pdfpages package and runs pdflatex
on it to obtain the output file

c) unlike psbook(1) it supports right-edge binding via optional "-r" 
switch, so the usage equivalent to the above two scripts is:

d) it has a hardcoded layout of nup=1x2 because that is what I needed,
but one can trivially change the source to have some other layout or
just use single-page layout and only re-arrange the page sequence, like
psbook(1) does.

# pdfbook -s32 -i gnt.pdf -o gnt-book.pdf # for European books
# pdfbook -s32 -r -i tnk.pdf -o tnk-book.pdf # for Hebrew books

I thought someone other than myself may find it useful, so why not post 
here...

Kind regards
Tigran

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

/*
 * pdfbook.c	Rearrange pages in a PDF file into signatures.
 *
 * Author	Tigran Aivazian <tigran at aivazian.fsnet.co.uk>
 *
 * Copyright:	GPLv2
 *
 * Based on the algorithm from psutils/psbook.c, which was
 * written by Angus J. C. Duggan 1991-1995.
 *
 * For example, to convert a file gnt.pdf into a book with
 * signature 32, we can use:
 *
 * $ pdfbook -s32 -i gnt.pdf -o gnt-book.pdf
 *
 */

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

static char *myname;

static void usage(void)
{
	fprintf(stderr, "\nUsage: %s [-s signature] [-r] [-i infile] [-o outfile]"
			"\n\t<signature> must be positive and divisible by 4\n", myname);
	exit(1);
}

static void swap_pairs(int array[], int len)
{
	int i;

	for(i=0; i<len; i+=2) {
		int tmp = array[i+1];

		array[i+1] = array[i];
		array[i] = tmp;
	}
}

/* no book has more than 1000000 pages, right? */
#define MAXPAGES	1000000
int actualpg[MAXPAGES];

int main(int argc, char *argv[])
{
	int i, c, signature = 0, npages = 0, maxpage;
	FILE *fp, *fout = NULL;
	static char cmdline[1024];
	char *outbase = NULL, *outtex = NULL, *outpdf = NULL, *infile = NULL;
	int quiet = 0, n = 0, rl = 0;

	myname = argv[0] = strdup(basename(argv[0]));

	while (EOF != (c = getopt(argc, argv, "s:ri:o:qr"))) {
		switch (c) {
			case 's':
				signature = atoi(optarg);
				if (signature < 1 || signature % 4)
					usage();
				break;

			case 'r':
				rl = 1; /* right to left typesetting */
				break;

			case 'i':
				infile = strdup(optarg);

				sprintf(cmdline, "pdfinfo %s | sed -ne \"s/Pages: *\\([0-9]*\\)/\\1/p\"", optarg);
				fp = popen(cmdline, "r");
				if (fscanf(fp, "%d", &npages) != 1) {
					fprintf(stderr, "%s: error reading \"%s\"\n", 
						myname, optarg);
					exit(1);
				}
				if (npages < 1) {
					fprintf(stderr, "%s: invalid number of pages=%d\n", 
						myname, npages);
					exit(1);
				}
				break;

			case 'o':
				outpdf = strdup(optarg);
				/* replace .pdf -> .tex */
				*strchr(outpdf, '.') = '\0';
				outbase=strdup(outpdf);
				strcat(outpdf, ".pdf");
				outtex=strdup(outpdf);
				*strchr(outtex, '.') = '\0';
				strcat(outtex, ".tex");

				fout = fopen(outtex, "w");
				if (!fout) {
					fprintf(stderr, "%s: error opening \"%s\" for write\n", 
						myname, outtex);
					exit(1);
				}
				break;

			case 'q':
				quiet = 1;
				break;

			default:
				usage();
		}
	}

	if (!infile) {
		fprintf(stderr, "%s: input file must be specified\n", myname);
		usage();
	}
	if (!fout) {
		fprintf(stderr, "%s: output file must be specified\n", myname);
		usage();
	}

	if (!signature)
		signature = maxpage = npages + (4 - npages%4)%4;
	else
		maxpage = npages + (signature - npages%signature)%signature;

	memset(actualpg, 0, MAXPAGES);
	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[12pt,a4paper,openany]{book}\n"
		      "\\usepackage{pdfpages}\n"
		      "\\begin{document}\n"
		      "\\includepdf[nup=1x2,landscape,pages={");
	if (rl)
		swap_pairs(actualpg, maxpage);

	for (i=0; i<maxpage; i++) {
		if (actualpg[i]) {
			fprintf(fout, "%d", actualpg[i]);
			if (!quiet)
				printf("[%d] ", actualpg[i]);
		} else {
			fprintf(fout, "{}");
			if (!quiet)
				printf("[*] ");
		}
		fprintf(fout, "%s", i<maxpage-1 ? "," : "");
		if (!quiet && (++n > 13)) {
			printf("\n");
			n = 0;
		}
	}

	fprintf(fout, "}]{%s}\n"
		      "\\end{document}\n", infile);
	if (!quiet) {
		printf("\n");
		printf("%s: Generating \"%s\" file now, please wait...\n", 
				myname, outpdf);
	}

	fclose(fout);
	sprintf(cmdline, "pdflatex %s > /dev/null 2>&1 < /dev/null", outtex);
	if (system(cmdline)) {
		fprintf(stderr, "%s: Failed to generate \"%s\" file\n",
				myname, outpdf);
		exit(1);
	}
	sprintf(cmdline, "rm -f %s.aux %s.tex %s.log", 
			outbase, outbase, outbase);
	system(cmdline);
	return 0;
}




More information about the pdftex mailing list