// 2-up saddle-stitch pdf imposition signature utility #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <error.h> #include <errno.h> #ifndef FALSE #define FALSE (0!=0) #define TRUE (0==0) #endif static int DEBUG = FALSE; /* Table 1: Paper Size w bp h bp w in h in w mm h mm A0b 2594 3370 36.03 46.81 915 1189 A0 2380 3368 33.06 46.78 840 1188 A1 1684 2380 23.39 33.06 594 840 A2 1190 1684 16.53 23.39 420 594 A3 842 1190 11.69 16.53 297 420 A4 595 842 8.26 11.69 210 297 A5 421 595 5.85 8.26 149 210 A6 298 421 4.14 5.85 105 149 A7 211 298 2.93 4.14 74 105 A8 149 211 2.07 2.93 53 74 A9 106 149 1.47 2.07 37 53 A10 75 106 1.04 1.47 26 37 */ #define A0b 0 // "{36.03in,46.81in}" #define A0 1 // "{33.06in,46.78in}" #define A1 2 // "{23.39in,33.06in}" #define A2 3 // "{16.53in,23.39in}" #define A3 4 // "{11.69in,16.53in}" #define TABLOID 5 // "{11in,17in}" #define LEGAL 6 // "{11in,14in}" #define LETTER 7 // "{8.5in,11in}" #define A4 8 // "{8.26in,11.6in}" #define A5 9 // "{5.85in,8.26in}" #define HALF_LETTER 10 // "{5.5,4.25in}" #define A6 11 // "{4.14in,5.85in}" #define A7 12 // "{2.93in,4.14in}" #define A8 13 // "{2.07in,2.93in}" #define A9 14 // "{1.47in,2.07in}" #define A10 15 // "{1.04in,1.47in}" #define SMALLEST A10 int two_up_size[] = { -1, // "A0b", -1, // "A0", A0, // "A1", A1, // "A2", A2, // "A3", A2, // "TABLOID", A2, // "LEGAL", TABLOID, // "LETTER", A3, // "A4", A4, // "A5", LETTER, // "HALF_LETTER", A5, // "A6", A6, // "A7", A7, // "A8", A8, // "A9", A9, // "A10", }; char *papername[] = { "a0b", "a0", "a1", "a2", "a3", "tabloid", "legal", "letter", "a4", "a5", "halfletter", "a6", "a7", "a8", "a9", "a10", }; char *papersize[] = { "{36.03in,46.81in}", // a0b "{33.06in,46.78in}", // a0 "{23.39in,33.06in}", // a1 "{16.53in,23.39in}", // a2 "{11.69in,16.53in}", // a3 "{11in,17in}", // tabloid "{11in,14in}", // legal "{8.5in,11in}", // letter "{8.26in,11.6in}", // a4 "{5.85in,8.26in}", // a5 "{5.5in,4.25in}", // half-letter "{4.14in,5.85in}", // a6 "{2.93in,4.14in}", // a7 "{2.07in,2.93in}", // a8 "{1.47in,2.07in}", // a9 "{1.04in,1.47in}", // a10 NULL }; float papersizef[] = { 36.03,46.81, // a0b 33.06,46.78, // a0 23.39,33.06, // a1 16.53,23.39, // a2 11.69,16.53, // a3 11.0,17.0, // tabloid 11.0,14.0, // legal 8.5,11.0, // letter 8.26,11.6, // a4 5.85,8.26, // a5 5.5,4.25, // half-letter 4.14,5.85, // a6 2.93,4.14, // a7 2.07,2.93, // a8 1.47,2.07, // a9 1.04,1.47, // a10 }; int main(int argc, char **argv) { int sheet = 1; int i, rc; static char pdfseparate[1024]; static char pdfinfo[1024]; static char twoup[1024]; static char landscape[1024]; static char portrait[1024]; int skip_cover = FALSE; int pages = 0, rotation, paper = -1; int base_page; FILE *info; float width, height; char *line = NULL; size_t len = 0; ssize_t nread; while (argc >= 2) { int p = SMALLEST; char pp[64]; int argpaper = -1; while (p >= 0) { sprintf(pp, "--%s", papername[p]); if (strcasecmp(argv[1], pp)==0) { argpaper = p; break; } p -= 1; } if (argpaper >= 0) { argv += 1; argc -= 1; paper = argpaper; // allow for more than one explicit paper assignment. Keep last. continue; } if (strcasecmp(argv[1], "--nocover")==0) { argv += 1; argc -= 1; skip_cover = TRUE; continue; } else if ((strcasecmp(argv[1], "--debug")==0) || (strcasecmp(argv[1], "-d")==0)) { argv += 1; argc -= 1; DEBUG = TRUE; continue; } else if ((argc >= 2) && ((strcasecmp(argv[1], "--11x17")==0) || (strcasecmp(argv[1], "--ledger")==0))) { argv += 1; argc -= 1; paper = TABLOID; continue; } else if (*argv[1] == '-') { fprintf(stderr, "syntax: layout [--nocover] file.pdf\n"); exit(1); } break; // remainder should be filename only } if (argc != 2) { fprintf(stderr, "syntax: layout [--nocover] file.pdf\n"); exit(1); } sprintf(pdfinfo, "pdfinfo %s", argv[1]); info = popen(pdfinfo, "r"); if (info == NULL) { fprintf(stderr, "layout: %s\n", strerror(errno)); exit(3); } while ((nread = getline(&line, &len, info)) != -1) { rc = sscanf(line, "Pages: %d\n", &pages); if (rc == 1) { if (skip_cover) { fprintf(stderr, "Document contains a cover (not to be included) and %d pages\n", pages-1); } else { fprintf(stderr, "Document contains %d pages\n", pages); } } rc = sscanf(line, "Page size: %f x %f pts\n", &width, &height); if (rc == 2) { fprintf(stderr, "Page size is %f by %f pts\n", width, height); if (paper < 0) { paper = SMALLEST; // pdfinfo TG.pdf|grep "^Page size:" // Page size: 610.56 x 773.52 pts #define ConvertToInches (36.03/2594) while (paper >= 0) { #ifdef NEVER fprintf(stderr, "Comparing %f x %f pts (%f x %f in) to %f x %f in (%s)\n", width, height, width*ConvertToInches, height*ConvertToInches, papersizef[paper*2], papersizef[paper*2+1], papername[paper]); #endif if ((width*ConvertToInches <= papersizef[paper*2]) && (height*ConvertToInches <= papersizef[paper*2+1])) { fprintf(stderr, "Assuming input paper option is --%s", papername[paper]); if (paper > 0) fprintf(stderr, ". Can be overridden, e.g. with --%s", papername[paper-1]); fprintf(stderr, "\n"); fprintf(stderr, "Output paper size will be %s %s\n", papername[two_up_size[paper]], papersize[two_up_size[paper]]); break; } paper -= 1; } } if (paper < 0) { paper = TABLOID; // my default fprintf(stderr, "Unknown large paper size - using --%s\n", papername[paper]); } } rc = sscanf(line, "Page rot: %d\n", &rotation); if (rc == 1) { if (rotation != 0) fprintf(stderr, "WARNING: "); // don't know if this is handled at all... untested... fprintf(stderr, "Rotation is %d\n", rotation); } } free(line); pclose(info); if (paper < 0) { fprintf(stderr, "layout: cannot determine input page size\?\n"); exit(4); } if (two_up_size[paper] < 0) { fprintf(stderr, "layout: no output paper large enough\?\n"); exit(4); } if (pages == 0) { fprintf(stderr, "layout: no pages to arrange\?\n"); exit(4); } system("rm -f foliotmp[0-9]*.pdf"); if (skip_cover) { sprintf(pdfseparate, "pdfseparate -f 2 %s foliotmp%%d.pdf", argv[1]); // pages numbered from 1 upwards. so cover sheet is page 1 pages -= 1; base_page = 2; } else { sprintf(pdfseparate, "pdfseparate %s foliotmp%%d.pdf", argv[1]); base_page = 1; } if (DEBUG) fprintf(stderr, "%s\n", pdfseparate); rc = system(pdfseparate); if (WIFSIGNALED(rc) && (WTERMSIG(rc) == SIGINT || WTERMSIG(rc) == SIGQUIT)) exit(rc); if (rc != 0) { if (errno != 0) fprintf(stderr, "layout: %s\n", strerror(errno)); exit(2); } // convert each pdf page to an image, // use imagemagik (convert) to threshold it to remove grey background (feature removed for now) // repackage back into same-sized pages // pdftoppm foliotmp69.pdf |pnmtopng -hist |convert png:- -sharpen 0x3 -white-threshold '90%' foliotmp69-new.pdf printf(" two-sided printing\n"); printf(" lower side upper side\n"); system("rm -f twoup[0-9]*.pdf"); for (i = 0; i < pages/2; i += 2) { printf("+-----+-----+ +-----+-----+\n"); printf("| | | | | |\n"); printf("| %02d | %02d | | %02d | %02d |\n", pages+1-(i+2)+1, i+0+1, i+1+1, pages+1-(i+3)+1); printf("| | | | | |\n"); printf("| | | | | |\n"); printf("+-----+-----+ +-----+-----+ sheet %d\n\n", sheet); sprintf(twoup, "/usr/bin/pdfjam --nup '2x1' --landscape --papersize '%s'" " --noautoscale 'true' --outfile twoup%04d-a.pdf -- foliotmp%0d.pdf - foliotmp%0d.pdf -%s", papersize[two_up_size[paper]], sheet, pages+1-(i+2)+(skip_cover ? 2 : 1), i+0+(skip_cover ? 2 : 1), DEBUG ? "" : " > /dev/null 2> /dev/null"); if (DEBUG) fprintf(stderr, "%s\n", twoup); system(twoup); sprintf(twoup, "/usr/bin/pdfjam --nup '2x1' --landscape --papersize '%s'" " --noautoscale 'true' --outfile twoup%04d-b.pdf -- foliotmp%0d.pdf - foliotmp%0d.pdf -%s", papersize[two_up_size[paper]], sheet, i+1+(skip_cover ? 2 : 1), pages+1-(i+3)+(skip_cover ? 2 : 1), DEBUG ? "" : " > /dev/null 2> /dev/null"); if (DEBUG) fprintf(stderr, "%s\n", twoup); system(twoup); sheet += 1; } if (DEBUG) { system("pdfjam twoup*.pdf"); } else { system("pdfjam twoup*.pdf > /dev/null 2> /dev/null"); } sprintf(landscape, "mv twoup%04d-b-pdfjam.pdf landscape-tmp.pdf", sheet-1); system(landscape); if (DEBUG) { system("cpdf -rotate 90 -i landscape-tmp.pdf -o portrait-tmp.pdf"); } else { system("cpdf -rotate 90 -i landscape-tmp.pdf -o portrait-tmp.pdf > /dev/null 2> /dev/null"); } // --landscape below? sprintf(twoup, "/usr/bin/pdfjam --papersize '%s' --twoside --scale 2.2 --outfile print-%s portrait-tmp.pdf%s", papersize[two_up_size[paper]], argv[1], DEBUG ? "" : " > /dev/null 2> /dev/null"); system(twoup); fprintf(stderr, "Output is in print-%s\n", argv[1]); if (!DEBUG) system("rm -f foliotmp[0-9]*.pdf twoup[0-9]*.pdf landscape-tmp.pdf portrait-tmp.pdf"); if (pages&3) { fprintf(stderr, "Warning: not a multiple of 4 pages. Need to tweak the code to add padding...\n"); exit(5); } exit(0); }