/* Time-stamp: <97/02/24 12:09:52 sluytman> * tiff2pdf.c * Link this program against Sam Leffler's libtiff * * Parts of this file are taken from tiff2psL2.c from tiff-v3.3 (?). */ /* * This file is part of Decomate, and is subject to the terms and * conditions of the GNU General Public License. See the file "COPYING" * in the main directory of this archive for more details. * * (c) 1997, Tilburg Univ, London School of Economics, Univ Autonoma Barcelona * * $Id: t2pdf.c,v 1.5 1998/11/23 19:12:33 alberto Exp alberto $ * * Modified by Alberto Accomazzi (aaccomazzi@cfa.harvard.edu), * NASA Astrophysics Data System, to incorporate the following changes: * * 1) support for some PDF 1.1 features, most notably the use of URI * links and binary data output * 2) support for labelling the document created with its bibliographic * code, passed to the program using the -L switch. Since this is * peculiar to our project, the relevant code is #ifdef'd * (define ADS_ARTICLES to compile this in). * * * $Log: t2pdf.c,v $ * Revision 1.5 1998/11/23 19:12:33 alberto * Added support for uncompressed TIFF, extra sanity checks are * made to avoid creating bad PDF image streams by concatenating * multi-strip compressed TIFF files or handling unknown compression * schemes. * * Revision 1.4 1998/11/23 15:28:12 alberto * Added support for G3 and LZW fax encoding (with minimal testing). * * Revision 1.3 1998/06/24 14:41:26 alberto * Added paper sizing and rescaling code (implementing option -P) * * Revision 1.2 1998/05/18 16:01:41 alberto * Cleaned up code to compile without warnings under gcc * * Revision 1.1 1997/10/24 22:28:19 alberto * Initial revision * * */ #include #include #include #include /* tiffio.h is distributed with the TIFF library * make sure your compiler finds it on its include search path */ #include #ifdef WIN32 #include #include #endif /* inch -> points multiplier factor (1 inch = 2.54 cm) */ #define PSDPI 72 /* extra margin that the page may have in width when computing * resolution based on image size and media size */ #define MARGIN 0.27 /* * letter-size definitions: * page is 612x792 points, 612 / 72 = 8.5 in = 21.59 cm * 792 / 72 = 11.0 in = 27.94 cm */ #define LETTER_PGSIZEX 8.5 #define LETTER_PGSIZEY 11.0 /* * A4 paper definitions: * page is 595x842 points, 595 / 72 = 8.26 in = 21.00 cm * 842 / 72 = 11.69 in = 29.70 cm */ #define A4_PGSIZEX 8.26 #define A4_PGSIZEY 11.69 /* * ledger-size definitions: * page is * page is 792x1224 points, 792 / 72 = 11.0 in = 27.94 cm * 1224 / 72 = 17.0 in = 43.18 cm */ #define LEDGER_PGSIZEX 11.0 #define LEDGER_PGSIZEY 17.0 /* * Ouput PDF file consists of: * * #ifdef ADS_ARTICLES * file header: * 1) font * 2) annotation * 3) label * #endif * * for each of N images: * 1) image * 2) length * 3) page * 4) translate * * file trailer: * 1) pages * 2) catalog * 3) type * 4) info */ #define CREATOR "NASA Astrophysics Data System" static char version[] = "$Id: t2pdf.c,v 1.5 1998/11/23 19:12:33 alberto Exp alberto $"; /* offset into page objects */ #define LENGTH 1 #define PAGE 2 #define TRANS 3 /* LINESIZE: multiple of 5, sometimes less in case of remainders */ #define LINESIZE 70 #ifndef MCHECK #define MCHECK(m) if (!m) { fprintf(stderr, "malloc failed\n"); exit(1); } #endif /* global output byte counter */ unsigned static long out_bytes = 0; /* function redefinitions */ #define PRINTF out_bytes += printf #define FWRITE out_bytes += fwrite #define PUTCHAR(a) putchar(a), out_bytes++ #define FTELL out_bytes /* define ASCIIHEXENCODE if you want to use the longer, but simpler, * ASCII HEX encoding filter rather than the ASCII 85 encoding filter */ #ifdef ASCIIHEXENCODE static char *hex = "0123456789abcdef"; #endif static char fontobj[] = "<< /Type /Font /Name /R1 /Subtype /Type1 /BaseFont /%s >>"; /* needs textstream length and string contents as sprintf arguments */ static char textobj[] = "\ << /Length %d >>\n\ stream\n\ %s\ endstream\n\ "; #define LABELFONT "Courier" #define LABELWIDTH 5 #define LABELHEIGHT 8 #define LABELX 26 #define LABELY 122.5 #define LINKX 20 #define LINKY 126 /* needs bibcode as sprintf arguments */ static char textstream[] = "\ q\n\ BT\n\ %lf %lf %lf rg\n\ /R1 %d Tf\n\ 0 1 -1 0 %lf %lf Tm\n\ (%s) Tj\n\ ET\n\ Q\n\ "; /* define ADS_ARTICLES for creating URI annotations of articles * which point to the appropriate ADS abstract URL */ #ifdef ADS_ARTICLES /* needs escaped URL as sprintf argument */ static char annobj[] = "\ << /Type /Annot\n\ /Rect [%lf %lf %lf %lf]\n\ /A <<\n\ /URI (%s)\n\ /S /URI\n\ >>\n\ /Subtype /Link\n\ /Border [0 0 1 [3]]\n\ /C [0 0 0.7]\n\ >>\n\ "; #endif /* ADS_ARTICLES */ char *pdf_date (void); void setup_page (float pw, float ph, int w, int h, float *sx, float *sy, float *tx, float *ty, float *bx, float *by); char *escape_url (char *url); char *http_hostname (void); static char usage[] = "\ Usage: %s [OPTIONS] file1 [file2 ...filen]\n\ OPTIONS:\n\ -b output binary data (default is encode using ASCII85 encoding)\n\ -C COMMENT output comment in upper left corner of page\n\ -paper SIZE output page is scaled to fit on SIZE paper, where SIZE is one of:\n\ letter, a4, ledger, or WxH (with W and H in inches) \n" #ifdef ADS_ARTICLES " -L LABEL output bibcode label and URL link in upper left corner of page\n" " -U URL make label a hypertext link to input URL\n" #endif ; int main(int argn, char **argv) { const char *img_name="Im"; const char *progname = argv[0]; char buff[BUFSIZ], *label = NULL, *link = NULL; float p_width = LETTER_PGSIZEX, p_height = LETTER_PGSIZEY; int objects, img_nr, i, obj_nr, firstobj, binary = 0; long *xref; while ((argv[1]) && argv[1][0] == '-') { if (strcmp(argv[1],"-L") == 0) { /* link label */ argv++; argn--; label = argv[1]; } else if (strcmp(argv[1],"-b") == 0) { /* binary output */ binary++; } else if (strcmp(argv[1],"-C") == 0) { /* comment label */ argv++; argn--; label = argv[1]; } else if (strcmp(argv[1],"-U") == 0) { /* comment label */ argv++; argn--; link = argv[1]; } else if (strcmp(argv[1],"-P") == 0) { /* paper size specified */ argv++; argn--; if (0 == strcasecmp(argv[1],"letter") || 0 == strcasecmp(argv[1],"a")) { p_width = LETTER_PGSIZEX; p_height = LETTER_PGSIZEY; } else if (0 == strcasecmp(argv[1],"ledger") || 0 == strcasecmp(argv[1],"tabloid") || 0 == strcasecmp(argv[1],"b")) { p_width = LEDGER_PGSIZEX; p_height = LEDGER_PGSIZEY; } else if (0 == strcasecmp(argv[1],"a4")) { p_width = A4_PGSIZEX; p_height = A4_PGSIZEY; } else if (2 == sscanf(argv[1], "%fx%f", &p_width, &p_height)) { /* custom page size */ } else { fprintf(stderr, "%s: invalid argument \"%s\" specified for option -paper\n", progname, argv[1]); fprintf(stderr, usage, progname); exit(1); } } else { fprintf(stderr, "%s: unrecognized option \"%s\"\n", progname, argv[1]); fprintf(stderr, usage, progname); exit(1); } argv++; argn--; } if (argn < 2) { fprintf(stderr, "%s: no input files specified\n", progname); fprintf(stderr, usage, progname); exit(2); } objects = 4 * (argn - 1) + 3; if (label) objects += 2; if (link) objects++; if (!(xref = calloc (objects+1, sizeof(unsigned long)))) { fprintf(stderr, "%s: error allocating xref buffer\n", progname); exit(3); } #ifdef WIN32 /* open stdout in binary mode to avoid having silly OS introduce \r before \n therefore screwing up the object lengths in PDF output */ if (-1 == _setmode(_fileno(stdout), _O_BINARY)) { fprintf(stderr, "%s: unable to set binary mode on stdout\n", progname); } #endif PRINTF("%%PDF-1.1\n%%\0300\0301\0302\0303\n"); obj_nr = 1; if (label) { double red = 0, green = 0, blue = 0; /* font object */ xref[obj_nr] = FTELL; sprintf(buff, fontobj, LABELFONT); PRINTF("%d 0 obj\n%s\nendobj\n", obj_nr++, buff); #ifdef ADS_ARTICLES if (link) { /* make text of link blue */ red = green = 0; blue = 1; } #endif /* label object */ xref[obj_nr] = FTELL; sprintf(buff, textstream, red, green, blue, LABELHEIGHT, (double)LABELX, (double)(p_height*PSDPI) - LABELY, label); PRINTF("%d 0 obj\n", obj_nr++); PRINTF(textobj, strlen(buff), buff); PRINTF("endobj\n"); #ifdef ADS_ARTICLES if (link) { /* annotation object */ xref[obj_nr] = FTELL; PRINTF("%d 0 obj\n", obj_nr++); PRINTF(annobj, (double)LINKX, (double)(p_height*PSDPI) - LINKY, (double)LINKX + LABELHEIGHT, (double)(p_height*PSDPI) - LINKY + (LABELWIDTH * (1 + strlen(label))), link); PRINTF("endobj\n"); } #endif /* increase total object counter */ objects += (obj_nr-1); } firstobj = obj_nr; for (img_nr = 1; img_nr < argn ; img_nr++, obj_nr += 4) { const char *cmptype = NULL; TIFF *tfFile; float x_bounding, y_bounding, x_scale, y_scale; float x_translate, y_translate; int width, height, key, flipcolors = 0; u_char *tf_buf, *in, out[5]; uint32 bufsize, *bc, chunk; uint16 fillorder; uint16 photometric, bitspersample; int remainder, loops, i, j, k = 0, strip, charCount, K = -1, params = 1; long streamsize; uint16 compression; uint32 group3opts; tstrip_t nstrips; /* Initializations */ x_bounding = y_bounding = 0.0; key = loops = strip = charCount = remainder = 0; width = height = 0; tf_buf = NULL; in = NULL; bc = NULL; if ((tfFile = TIFFOpen( argv[img_nr], "r") ) == NULL) { fprintf(stderr, "Unable to open input file %s\n", argv[img_nr]); exit(1); } TIFFGetField(tfFile, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(tfFile, TIFFTAG_IMAGELENGTH, &height); if ((width == 0) || (height == 0)) { fprintf(stderr, "Incorrect width and/or height in TIFF file!\n"); TIFFClose(tfFile); exit(1); } TIFFGetField(tfFile, TIFFTAG_COMPRESSION, &compression); /* next switch statement from tiff2ps */ #define P(a,b) (((a)<<4)|((b)&0xf)) switch (P(compression, photometric)) { case P(COMPRESSION_CCITTRLE, PHOTOMETRIC_MINISBLACK): case P(COMPRESSION_CCITTRLE, PHOTOMETRIC_MINISWHITE): cmptype = "/CCITTFaxDecode"; K = 0; break; case P(COMPRESSION_CCITTFAX3, PHOTOMETRIC_MINISBLACK): case P(COMPRESSION_CCITTFAX3, PHOTOMETRIC_MINISWHITE): cmptype = "/CCITTFaxDecode"; TIFFGetField(tfFile, TIFFTAG_GROUP3OPTIONS, &group3opts); K = group3opts & GROUP3OPT_2DENCODING; break; case P(COMPRESSION_CCITTFAX4, PHOTOMETRIC_MINISBLACK): case P(COMPRESSION_CCITTFAX4, PHOTOMETRIC_MINISWHITE): cmptype = "/CCITTFaxDecode"; K = -1; break; case P(COMPRESSION_LZW, PHOTOMETRIC_MINISWHITE): flipcolors = 1; case P(COMPRESSION_LZW, PHOTOMETRIC_MINISBLACK): cmptype = "/LZWDecode"; params = 0; break; case P(COMPRESSION_NONE, PHOTOMETRIC_MINISWHITE): flipcolors = 1; case P(COMPRESSION_NONE, PHOTOMETRIC_MINISBLACK): cmptype = ""; params = 0; break; default: fprintf(stderr, "%s: compression mode %d not supported\n", argv[img_nr], compression); continue; } #undef P nstrips = TIFFNumberOfStrips(tfFile); if (nstrips > 1 && compression != COMPRESSION_NONE) { fprintf(stderr, "%s: multi-strip compressed TIFF not supported\n", argv[img_nr]); continue; } /* next 2 lines from tiff2ps.c */ TIFFGetFieldDefaulted(tfFile, TIFFTAG_BITSPERSAMPLE, &bitspersample); TIFFGetField(tfFile, TIFFTAG_PHOTOMETRIC, &photometric); setup_page (p_width, p_height, width, height, &x_scale, &y_scale, &x_translate, &y_translate, &x_bounding, &y_bounding); xref[obj_nr] = FTELL; /* image object */ PRINTF("%d 0 obj\n", obj_nr); PRINTF("<<\n/Type /XObject\n/Subtype /Image\n/Name /%s%d\n/Filter [ ", img_name, img_nr); if (!(binary)) #ifdef ASCIIHEXENCODE PRINTF("/ASCIIHexDecode "); #else PRINTF("/ASCII85Decode "); #endif PRINTF("%s ]\n/Width %d /Height %d /BitsPerComponent %d\n", cmptype, width, height, bitspersample); PRINTF("/ColorSpace /DeviceGray\n/Length %d 0 R\n/DecodeParms [ ", obj_nr + LENGTH); if (!(binary)) PRINTF("null "); if (params) PRINTF("<< /K %d /Columns %d /Rows %d /EndOfBlock false /BlackIs1 %s >> ", K, width, height, (photometric == PHOTOMETRIC_MINISBLACK) ? "true" : "false"); else if (cmptype && cmptype[0]) PRINTF("null "); PRINTF("]\n"); if (flipcolors) PRINTF("/Decode [ 1 0 ]\n"); PRINTF(">>\nstream\n"); streamsize = FTELL; TIFFGetField(tfFile, TIFFTAG_FILLORDER, &fillorder); TIFFGetField(tfFile, TIFFTAG_STRIPBYTECOUNTS, &bc); bufsize = bc[0] + 4; tf_buf = (u_char *) malloc(bufsize); MCHECK(tf_buf); for (strip = remainder = j = 0; strip < nstrips; strip++) { if (bc[strip] > bufsize) { tf_buf = (u_char *) realloc(tf_buf, bc[strip] + 4); MCHECK(tf_buf); bufsize = bc[strip]; } charCount = TIFFReadRawStrip(tfFile, strip, tf_buf + remainder, bc[strip]); if (charCount < 0) { TIFFClose(tfFile); free(tf_buf); exit(1); } if (fillorder == FILLORDER_LSB2MSB) TIFFReverseBits(tf_buf + remainder, charCount); charCount += remainder; if ((binary)) { FWRITE(tf_buf, 1, charCount, stdout); continue; } #ifdef ASCIIHEXENCODE for (in = tf_buf, i = 0; i < charCount; i++, in++) { PUTCHAR(hex[(*in >> 4) & 0xF]); PUTCHAR(hex[*in & 0xF]); k += 2; if (k >= LINESIZE) { PUTCHAR('\n'); k = 0; } } #else remainder = charCount % 4; loops = charCount / 4; for (in = tf_buf, i = 0; i < loops; i++) { chunk = ((in[0] * 256 + in[1]) * 256 + in[2]) * 256 + in[3]; if (chunk == 0) { PUTCHAR('z'); k++; } else { for (j=4; j>=0; j--) { out[j] = chunk % 85 + 33; chunk = chunk / 85; } PUTCHAR(out[0]); PUTCHAR(out[1]); PUTCHAR(out[2]); PUTCHAR(out[3]); PUTCHAR(out[4]); k += 5; } if (k >= LINESIZE ) { PUTCHAR('\n'); k = 0; } in += 4; } #endif } #ifndef ASCIIHEXENCODE switch (remainder) { case 1: chunk = in[0]; for (i=4; i>=0; i--) { out[i] = chunk % 85 + 33; chunk = chunk / 85; } PUTCHAR(out[3]); PUTCHAR(out[4]); break; case 2: chunk = (in[0] * 256) + in[1]; for (i=4; i>=0; i--) { out[i] = chunk % 85 + 33; chunk = chunk / 85; } PUTCHAR(out[2]); PUTCHAR(out[3]); PUTCHAR(out[4]); break; case 3: chunk = ((in[0] * 256) + in[1]) * 256 + in[2]; for (i=4; i>=0; i--) { out[i] = chunk % 85 + 33; chunk = chunk / 85; } PUTCHAR(out[1]); PUTCHAR(out[2]); PUTCHAR(out[3]); PUTCHAR(out[4]); break; default: break; } #endif if (!(binary)) PRINTF("~>"); /* EOD marker */ TIFFClose(tfFile); free(tf_buf); streamsize = FTELL - streamsize; PRINTF("\nendstream\nendobj\n"); /* stream-size object */ xref[obj_nr+LENGTH] = FTELL; PRINTF("%d 0 obj\n%ld\nendobj\n", obj_nr+LENGTH, streamsize); /* page object */ xref[obj_nr+PAGE] = FTELL; PRINTF( "%d 0 obj\n<<\n/Type /Page\n/Parent %d 0 R\n", obj_nr+PAGE, objects-2); PRINTF("/Resources <<\n/XObject << /%s%d %d 0 R >>\n" "/ProcSet [ /PDF /ImageB ", img_name, img_nr, obj_nr); if (label) PRINTF("/Text ]\n/Font << /R1 1 0 R >>\n>>\n"); else PRINTF("]\n>>\n"); PRINTF("/Contents [ %d 0 R", obj_nr+TRANS); if (label) PRINTF(" 2 0 R"); PRINTF(" ]\n"); #ifdef ADS_ARTICLES if (label && link) PRINTF("/Annots [ 3 0 R ]\n"); #endif PRINTF(">>\nendobj\n"); /* page */ /* size and scale data, printed first to a buffer, so the length can be calculated */ sprintf(buff, "q %f 0 0 %f %f %f cm /%s%d Do Q\n" , x_bounding, y_bounding, x_translate, y_translate, img_name, img_nr); xref[obj_nr+TRANS] = FTELL; PRINTF("%d 0 obj\n<< /Length %d >>\nstream\n%sendstream\nendobj\n", obj_nr + TRANS, (int)strlen(buff), buff); } /* pages object */ xref[objects-2] = FTELL; PRINTF("%d 0 obj\n<<\n/Kids [\n", objects - 2); for (obj_nr = firstobj, i = 1; i < argn; obj_nr += 4, i++) { PRINTF("%d 0 R\n", obj_nr+PAGE); } PRINTF( "]\n/Count %d\n/Type /Pages\n/MediaBox [ 0 0 %d %d ]\n>>\nendobj\n", argn-1, (int)(p_width*PSDPI), (int)(p_height*PSDPI) ) ; /* catalog object */ xref[objects-1] = FTELL; PRINTF("%d 0 obj\n<<\n/Pages %d 0 R\n/Type /Catalog\n" "/OpenAction [ %d 0 R /FitH %d ]\n>>\nendobj\n", objects-1, objects-2, firstobj + 2, (int)(p_height*PSDPI)); /* info object */ xref[objects] = FTELL; PRINTF("%d 0 obj\n<<\n/CreationDate %s\n/Producer (%s - %s)\n>>\nendobj\n", objects, pdf_date(), CREATOR, version); /* xref */ xref[0] = FTELL; PRINTF("xref\n0 %d\n", objects+1); /* extra space at end to get 20 characters (not needed if \n == 0xd0xa) */ PRINTF( "0000000000 65535 f \n"); for (i = 1; i <= objects; i++) { PRINTF("%010ld 00000 n \n", xref[i]); } /* trailer */ PRINTF("trailer\n<<\n/Size %d\n/Root %d 0 R\n/Info %d 0 R\n", objects+1, objects-1, objects) ; PRINTF(">>\nstartxref\n%ld\n%%%%EOF\n", xref[0]); fflush(stdout); free(xref); exit(0); } char *pdf_date (void) { #include time_t now; struct tm *local_tm; char *buff = (char *) malloc (32L); now = time(NULL); local_tm = localtime(&now); /* (D:YYYYMMDDHHmmSS) */ strftime(buff, 32L, "(D:%Y%m%d%H%M%S)", local_tm); return(buff); } void setup_page (float pw, float ph, int w, int h, float *sx, float *sy, float *tx, float *ty, float *bx, float *by) { int xres, yres; double res; #ifndef max #define max(x,y) (((x)>(y))?(x):(y)) #endif /* calculate resolution (assume it's a multiple of 25 dpi) */ for (xres = 100; ((double)w/(double)xres) > pw + MARGIN; xres += 25) {} for (yres = 100; ((double)h/(double)yres) > ph; yres += 25) {} res = max(xres,yres); *sx = *sy = (double)PSDPI / res; *tx = (pw - (double)w / res) * PSDPI / 2.0; *ty = (ph - (double)h / res) * PSDPI / 2.0; *bx = *sx * w; *by = *sy * h; } char *escape_url (char *url) { register int x,y; char *copy; copy = (char *) malloc (3 * strlen(url) + 1); if (!(copy)) return(NULL); #define c2x(what,where) sprintf((where),"%%%02x",(what)) for (x = 0, y = 0; url[y]; x++, y++) { if ((strchr ("% ?+&", (copy[x] = url[y])))) { c2x (url[y], ©[x]); x += 2; } } copy[x] = '\0'; return(copy); } char *http_hostname (void) { char *h = NULL, *r = NULL; if ((h = getenv("HTTP_HOST"))) {} else if ((h = getenv("SERVER_NAME"))) {} else if ((h = getenv("HOST"))) {} else return NULL; r = (char *) malloc (strlen(h) + 1); strcpy(r, h); return r; }