dcj@bar.zk3.dec.com (Dave Jedlinsky OSEM) (03/23/90)
# Eat this, line-eater! Enclosed is the source to gif2ps, which converts GIF files to PostScript. It is based on xgif, by John Bradley. The current implementation has a few nice features, and there's always room for improvement. The generated PostScript will work on all printers if the original GIF file is grayscale, but a color picture requires that the printer support the colorimage operator. -David Jedlinsky Digital Equipment Corporation | dcj@decvax.dec.com | This space intentionally left blank. ...!decwrl!decvax!dcj | -----------------------------Cut Here------------------------------- #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./AUTHOR` then echo "writing ./AUTHOR" cat > ./AUTHOR << '\End\Of\Shar\' gif2ps converted by David C. Jedlinsky (dcj@decvax.dec.com), based on the program xgif, by John Bradley. -dcj --- Written by John Bradley (bradley@cis.upenn.edu), one of the fine folks at the University of Pennsylvania. Do whatever you want with this program, though it *would* be nice if my name remained on it somewhere... Other than that, it may be freely modified, distributed, and used to fill up disk space. --jhb \End\Of\Shar\ else echo "will not over write ./AUTHOR" fi if `test ! -s ./Makefile` then echo "writing ./Makefile" cat > ./Makefile << '\End\Of\Shar\' # # Makefile for gif2ps # OBJS = \ psgif.o \ psgifload.o SRCS = \ psgif.c \ psgifload.c .c.o: cc -c $*.c gif2ps: ${OBJS} cc -o gif2ps ${OBJS} psgifload.o: psgifload.c psgif.h psgif.o: psgif.c psgif.h clean: rm -rf *.o *~ gif2ps \End\Of\Shar\ else echo "will not over write ./Makefile" fi if `test ! -s ./README` then echo "writing ./README" cat > ./README << '\End\Of\Shar\' gif2ps is a program that converts GIF images to a form that can be printed on an Postscript printer. The images can be freely rescaled. Based (heavily) on xgif, by John Bradley. -David Jedlinsky (dcj@decvax.dec.com) --- A collection of GIF images is available via anonymous ftp to "dsl.cis.upenn.edu" [128.91.2.12]. John Bradley - bradley@cis.upenn.edu --- Revision History: Removed X code, added PostScript output (dcj, 3/20/90). Original version, converted from xgif, patchlevel 2 (2/13/89). \End\Of\Shar\ else echo "will not over write ./README" fi if `test ! -s ./gif2ps.1` then echo "writing ./gif2ps.1" cat > ./gif2ps.1 << '\End\Of\Shar\' .TH gif2ps 1 .SH NAME gif2ps \- converts GIF (*) pictures to PostScript (**) .SH SYNTAX \fBgif2ps\fP [-c] [-i] \fIfilename\fP [-l] [-o \fIfilename\fP] [-s \fIscale\fP] .SH DESCRIPTION \fBgif2ps\fP is a conversion program which converts GIF images to the PostScript language. .SH OPTIONS The '-c' option changes the output to a more space-efficient compact representation. The drawback of this option is that the computation time on the side of the printer is increased by approximately a factor of five. It is most useful on a printer with a fast processor and a slow serial interface, as it reduces the time to actually download the picture. .PP The '-i' option specifies the input filename. The '-i' itself is optional, and is provided only for symmetry with the '-o' option. .PP The '-l' option renders the image in landscape mode (the image is rotated 90 degrees from the orientation specified in the GIF file). .PP The '-o' option specifies the output filename. If \fIfilename\fP is not supplied, the PostScript code will be written to stdout. .PP The '-s' option specifies a scale factor, which must be greater than zero. \fIScale\fP can be a floating point number, the value defaults to 1. .PP If the GIF image consists solely of grayscale colors, the generated PostScript output will use the more efficient \fIimage\fP operator to render the image. .SH LIMITATIONS Standard input is not a valid input file in the current implementation. .PP If the GIF image is not a grayscale image, the printer must support the \fIcolorimage\fP operator. The generated code will cause a printer which does not support the operator to print an error message and quit. .PP This program ignores 'local colormaps' in GIF files (see the GIF spec for details). It also only displays the first image in GIF files that have multiple images in them. .PP .SH AUTHOR David Jedlinsky (dcj@decvax.dec.com), based (heavily) on xgif, by John Bradley (bradley@cis.upenn.edu), which is in turn based (heavily) on gif2ras.c, by Patrick J. Naughton (naughton@wind.sun.com), a program that converts GIF pictures to Sun Rasterfiles. .PP (*) GIF is a no doubt a trademark of CompuServe, so watch it! .PP (**) PostScript is a trademark of Adobe Systems Incorporated.\End\Of\Shar\ else echo "will not over write ./gif2ps.1" fi if `test ! -s ./psgif.c` then echo "writing ./psgif.c" cat > ./psgif.c << '\End\Of\Shar\' /* * gif2ps.c - converts GIF pictures to Postscript. * * Author: David Jedlinsky, Digital Equipment Corporation * (dcj@decvax.dec.com) * John Bradley, University of Pennsylvania * (bradley@cis.upenn.edu) */ #define MAIN #include "psgif.h" /*******************************************/ main(argc, argv) int argc; char *argv[]; /*******************************************/ { int i; char *in, *out; FILE *outp; cmd = argv[0]; in = out = NULL; compact = 0; landscape = 0; scale = 1.0; /*********************Options*********************/ for (i = 1; i < argc; i++) { if (!strncmp(argv[i], "-c", 2)) { /* compact */ compact = 1; continue; } if (!strncmp(argv[i], "-l", 2)) { /* landscape */ landscape = 1; continue; } if (!strncmp(argv[i], "-s", 2)) { /* scale */ if (++i >= argc) Syntax(cmd); if ((scale = atof(argv[i])) == 0.0) { fprintf(stderr, "%s: Invalid scale.\n", cmd); exit(1); } continue; } if (!strncmp(argv[i], "-i", 2)) { /* input */ if (++i >= argc) Syntax(cmd); in = argv[i]; continue; } if (!strncmp(argv[i], "-o", 2)) { /* output */ if (++i >= argc) Syntax(cmd); out = argv[i]; continue; } if (argv[i][0] != '-') { /* the file name */ in = argv[i]; continue; } Syntax(cmd); } if (in == NULL) { /* in="-"; */ Syntax(cmd); } /********** Open File/Write out Header Information********/ if (out==NULL) { out = "stdout"; outp=stdout; } else { outp=fopen(out, "w"); } fprintf(outp, "%%!PS-Adobe-1.0\n"); fprintf(outp, "%%%%Title: %s\n", out); fprintf(outp, "%%%%Creator: %s\n", cmd); fprintf(outp, "%%%%Pages: (atend)\n"); fprintf(outp, "%%%%EndComments\n\n"); /* Add macros needed here. */ fprintf(outp, "%% GIF macros\n\n"); fprintf(outp, "%%%%EndProlog\n\n"); /***************** Open/Read the File *****************/ LoadGIF(in, outp); /*************** Write/Close Output File ***************/ fprintf(outp, "%%%%Trailer\n"); fprintf(outp, "%%%%Pages: 1\n"); if (outp != stdout) if (fclose(outp) == EOF) fprintf(stderr, "%s: error closing %s.\n", cmd, out); } /***********************************/ Syntax(cmd) char *cmd; { fprintf(stderr, "Usage: %s [-c] [-i] infile [-l] [-s scale] [-o outfile]\n",cmd); exit(1); } /***********************************/ FatalError (identifier) char *identifier; { fprintf(stderr, "%s: %s\n",cmd, identifier); exit(-1); } \End\Of\Shar\ else echo "will not over write ./psgif.c" fi if `test ! -s ./psgif.h` then echo "writing ./psgif.h" cat > ./psgif.h << '\End\Of\Shar\' /* * psgif.h - header file for gif2ps, but you probably already knew as much */ #define REVDATE "Rev: 10/26/89" /* include files */ #include <stdio.h> #include <math.h> #include <ctype.h> #ifndef MAIN #define WHERE extern #else #define WHERE #endif typedef unsigned char byte; void *malloc(), exit(), _exit(), free(); /* global vars */ WHERE int numcols,compact,bw,landscape; WHERE unsigned long cols[256]; WHERE char *cmd; WHERE double scale; #ifndef False #define False 0 #define True !False #endif \End\Of\Shar\ else echo "will not over write ./psgif.h" fi if `test ! -s ./psgifload.c` then echo "writing ./psgifload.c" cat > ./psgifload.c << '\End\Of\Shar\' /* * psgifload.c - based strongly on... * * gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun Raster image. * * Copyright (c) 1988, 1989 by Patrick J. Naughton * * Author: Patrick J. Naughton * naughton@wind.sun.com * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * */ #include "psgif.h" typedef int boolean; #define NEXTBYTE (*ptr++) #define IMAGESEP 0x2c #define INTERLACEMASK 0x40 #define COLORMAPMASK 0x80 FILE *fp; int BitOffset = 0, /* Bit Offset of next code */ XC = 0, YC = 0, /* Output X and Y coords of current pixel */ Pass = 0, /* Used by output routine if interlaced pic */ OutCount = 0, /* Decompressor output 'stack count' */ RWidth, RHeight, /* screen dimensions */ Width, Height, /* image dimensions */ LeftOfs, TopOfs, /* image offset */ BitsPerPixel, /* Bits per pixel, read from GIF header */ BytesPerScanline, /* bytes per scanline in output raster */ ColorMapSize, /* number of colors */ Background, /* background color */ CodeSize, /* Code size, read from GIF header */ InitCodeSize, /* Starting code size, used during Clear */ Code, /* Value returned by ReadCode */ MaxCode, /* limiting value for current code size */ ClearCode, /* GIF clear code */ EOFCode, /* GIF end-of-information code */ CurCode, OldCode, InCode, /* Decompressor variables */ FirstFree, /* First free code, generated per GIF spec */ FreeCode, /* Decompressor, next free slot in hash table */ BitMask, /* AND mask for data size */ ReadMask; /* Code AND mask for current code size */ byte FinChar; /* Decompressor variable */ boolean Interlace, HasColormap; boolean Verbose = False; byte *Image; /* The result array */ byte *RawGIF; /* The heap array to hold it, raw */ byte *Raster; /* The raster data stream, unblocked */ /* The hash table used by the decompressor */ int Prefix[4096]; int Suffix[4096]; /* An output array used by the decompressor */ byte OutCode[1025]; /* The color map, read from the GIF header */ byte Red[256], Green[256], Blue[256], used[256]; int numused; char *id = "GIF87a"; /*****************************/ LoadGIF(fname, outp) char *fname; FILE *outp; /*****************************/ { int filesize; register byte ch, ch1; register byte *ptr, *ptr1; register int i; /* stdin won't work with the current implementation! */ /* if (strcmp(fname,"-") == 0) { fp = stdin; fname = "<stdin>"; } else*/ fp = fopen(fname,"r"); if (!fp) FatalError("file not found"); /* find the size of the file */ if (fseek(fp, 0L, 2) != 0) FatalError("couldn't seek to end of file"); filesize = ftell(fp); if (fseek(fp, 0L, 0) != 0) FatalError("couldn't seek to start of file"); if (!(ptr = RawGIF = (byte *) malloc((size_t)filesize))) FatalError("not enough memory to read gif file"); if (!(Raster = (byte *) malloc((size_t)filesize))) FatalError("not enough memory to read gif file"); if (fread((void *)ptr, (size_t)filesize, 1, fp) != (size_t)1) FatalError("GIF data read failed"); if (strncmp((char *)ptr, id, 6)) FatalError("not a GIF file"); ptr += 6; /* Get variables from the GIF screen descriptor */ ch = NEXTBYTE; RWidth = ch + 0x100 * NEXTBYTE; /* screen dimensions... not used. */ ch = NEXTBYTE; RHeight = ch + 0x100 * NEXTBYTE; if (Verbose) fprintf(stderr, "screen dims: %dx%d.\n", RWidth, RHeight); ch = NEXTBYTE; HasColormap = ((ch & COLORMAPMASK) ? True : False); BitsPerPixel = (ch & 7) + 1; numcols = ColorMapSize = 1 << BitsPerPixel; BitMask = ColorMapSize - 1; Background = NEXTBYTE; /* background color... not used. */ if (NEXTBYTE) /* supposed to be NULL */ FatalError("corrupt GIF file (bad screen descriptor)"); /* Read in global colormap. */ if (HasColormap) { if (Verbose) fprintf(stderr, "%s is %dx%d, %d bits per pixel, (%d colors).\n", fname, Width,Height,BitsPerPixel, ColorMapSize); for (i = 0; i < ColorMapSize; i++) { Red[i] = NEXTBYTE; Green[i] = NEXTBYTE; Blue[i] = NEXTBYTE; used[i] = 0; } numused = 0; } else { /* no colormap in GIF file */ fprintf(stderr,"%s: warning! no colortable in this file. Winging it.\n",cmd); if (!numcols) numcols=256; for (i=0; i<numcols; i++) cols[i] = (unsigned long) i; } /* Check for image seperator */ if (NEXTBYTE != IMAGESEP) FatalError("corrupt GIF file (no image separator)"); /* Now read in values from the image descriptor */ ch = NEXTBYTE; LeftOfs = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; TopOfs = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; Width = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; Height = ch + 0x100 * NEXTBYTE; Interlace = ((NEXTBYTE & INTERLACEMASK) ? True : False); if (Verbose) fprintf(stderr, "Reading a %d by %d %sinterlaced image...", Width, Height, (Interlace) ? "" : "non-"); else fprintf(stderr, "%s: %s is %dx%d, %d colors ", cmd, fname, Width,Height,ColorMapSize); /* Note that I ignore the possible existence of a local color map. * I'm told there aren't many files around that use them, and the spec * says it's defined for future use. This could lead to an error * reading some files. */ /* Start reading the raster data. First we get the intial code size * and compute decompressor constant values, based on this code size. */ CodeSize = NEXTBYTE; ClearCode = (1 << CodeSize); EOFCode = ClearCode + 1; FreeCode = FirstFree = ClearCode + 2; /* The GIF spec has it that the code size is the code size used to * compute the above values is the code size given in the file, but the * code size used in compression/decompression is the code size given in * the file plus one. (thus the ++). */ CodeSize++; InitCodeSize = CodeSize; MaxCode = (1 << CodeSize); ReadMask = MaxCode - 1; /* Read the raster data. Here we just transpose it from the GIF array * to the Raster array, turning it from a series of blocks into one long * data stream, which makes life much easier for ReadCode(). */ ptr1 = Raster; do { ch = ch1 = NEXTBYTE; while (ch--) *ptr1++ = NEXTBYTE; if ((ptr1 - Raster) > filesize) { FatalError("corrupt GIF file (unblock)"); } } while(ch1); free((void *)RawGIF); /* We're done with the raw data now... */ if (Verbose) { fprintf(stderr, "done.\n"); fprintf(stderr, "Decompressing..."); } /* Allocate buffer to hold decompressed image */ Image = (byte *)malloc((size_t)Width*Height); if (!Image) FatalError("not enough memory for Image"); BytesPerScanline = Width; /* Decompress the file, continuing until you see the GIF EOF code. * One obvious enhancement is to add checking for corrupt files here. */ Code = ReadCode(); while (Code != EOFCode) { /* Clear code sets everything back to its initial value, then reads the * immediately subsequent code as uncompressed data. */ if (Code == ClearCode) { CodeSize = InitCodeSize; MaxCode = (1 << CodeSize); ReadMask = MaxCode - 1; FreeCode = FirstFree; CurCode = OldCode = Code = ReadCode(); FinChar = CurCode & BitMask; AddToPixel(FinChar); } else { /* If not a clear code, then must be data: save same as CurCode and InCode */ CurCode = InCode = Code; /* If greater or equal to FreeCode, not in the hash table yet; * repeat the last character decoded */ if (CurCode >= FreeCode) { CurCode = OldCode; OutCode[OutCount++] = FinChar; } /* Unless this code is raw data, pursue the chain pointed to by CurCode * through the hash table to its end; each code in the chain puts its * associated output code on the output queue. */ while (CurCode > BitMask) { if (OutCount > 1024) { fprintf(stderr,"\nCorrupt GIF file (OutCount)!\n"); _exit(-1); /* calling 'exit(-1)' dumps core, so I don't */ } OutCode[OutCount++] = Suffix[CurCode]; CurCode = Prefix[CurCode]; } /* The last code in the chain is treated as raw data. */ FinChar = CurCode & BitMask; OutCode[OutCount++] = FinChar; /* Now we put the data out to the Output routine. * It's been stacked LIFO, so deal with it that way... */ for (i = OutCount - 1; i >= 0; i--) AddToPixel(OutCode[i]); OutCount = 0; /* Build the hash table on-the-fly. No table is stored in the file. */ Prefix[FreeCode] = OldCode; Suffix[FreeCode] = FinChar; OldCode = InCode; /* Point to the next slot in the table. If we exceed the current * MaxCode value, increment the code size unless it's already 12. If it * is, do nothing: the next code decompressed better be CLEAR */ FreeCode++; if (FreeCode >= MaxCode) { if (CodeSize < 12) { CodeSize++; MaxCode *= 2; ReadMask = (1 << CodeSize) - 1; } } } Code = ReadCode(); } free((void *)Raster); if (Verbose) fprintf(stderr, "done.\n"); else fprintf(stderr,"(of which %d are used)\n",numused); if (fp != stdin) if (fclose(fp) == EOF) fprintf(stderr, "%s: error closing %s.\n", cmd, fname); ColorDicking(fname); WriteGIFdata(outp); } /* Fetch the next code from the raster data stream. The codes can be * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to * maintain our location in the Raster array as a BIT Offset. We compute * the byte Offset into the raster array by dividing this by 8, pick up * three bytes, compute the bit Offset into our 24-bit chunk, shift to * bring the desired code to the bottom, then mask it off and return it. */ ReadCode() { int RawCode, ByteOffset; ByteOffset = BitOffset / 8; RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]); if (CodeSize >= 8) RawCode += (0x10000 * Raster[ByteOffset + 2]); RawCode >>= (BitOffset % 8); BitOffset += CodeSize; return(RawCode & ReadMask); } AddToPixel(Index) byte Index; { if (YC<Height) *(Image + YC * BytesPerScanline + XC) = Index; if (!used[Index]) { used[Index]=1; numused++; } /* Update the X-coordinate, and if it overflows, update the Y-coordinate */ if (++XC == Width) { /* If a non-interlaced picture, just increment YC to the next scan line. * If it's interlaced, deal with the interlace as described in the GIF * spec. */ XC = 0; if (!Interlace) YC++; else { switch (Pass) { case 0: YC += 8; if (YC >= Height) { Pass++; YC = 4; } break; case 1: YC += 8; if (YC >= Height) { Pass++; YC = 2; } break; case 2: YC += 4; if (YC >= Height) { Pass++; YC = 1; } break; case 3: YC += 2; break; default: break; } } } } /*************************/ ColorDicking(fname) char *fname; { int i; bw = 1; for (i = 0; i < ColorMapSize; i++) if (used[i] && ((Red[i] != Green[i]) || (Red[i] != Blue[i]) || (Green[i] != Blue[i]))) bw = 0; if (bw) fprintf(stderr, "%s: %s is a grayscale image.\n", cmd, fname); } /*************************/ WriteGIFdata(fp) FILE *fp; { int i, j, count = 1; double sx, sy, tx, ty, tmp; byte *ptr = Image; fprintf(fp, "%%%%Page: 1 1\n\n"); sx = (double)Width * scale; sy = (double)Height * scale; if (landscape) { tx = 306.0 + sy / 2; ty = 396.0 - sx / 2; tmp = sx; sx = sy; sy = tmp; } else { tx = 306.0 - sx / 2; ty = 396.0 - sy / 2; } if (!bw) { fprintf(fp, "systemdict /colorimage known\n"); fprintf(fp, "userdict /colorimage known\n"); fprintf(fp, "or not\n"); fprintf(fp, "{\n"); fprintf(fp, " /Times-Roman findfont 20 scalefont setfont\n"); fprintf(fp, " 72 72 moveto\n"); fprintf(fp, " (This picture requires the colorimage operator.) show\n"); fprintf(fp, " showpage stop\n"); fprintf(fp, "} if\n\n"); if (compact) { fprintf(fp, "/ColorMap %d array def\n", ColorMapSize); for (i=0; i<ColorMapSize; i++) { if (i % 8 == 0) fprintf(fp, "\n"); fprintf(fp, "<%02x%02x%02x> ", Red[i], Green[i], Blue[i]); } fprintf(fp, "\nColorMap astore\n\n"); fprintf(fp, "/rgbstr 3 string def\n"); fprintf(fp, "/nextch 1 string def\n"); /* Center the image on a 8.5x11 sheet of paper */ fprintf(fp, "%.1f %.1f translate\n", tx, ty); fprintf(fp, "%.1f %.1f scale\n", sx, sy); if (landscape) fprintf(fp, "90 rotate\n"); fprintf(fp, "%d %d 8\n", Width, Height); fprintf(fp, "[%d 0 0 %d 0 %d]\n", Width, -Height, Height); fprintf(fp, "{\n"); fprintf(fp, " currentfile nextch readhexstring\n"); fprintf(fp, " {\n"); fprintf(fp, " 0 get\n"); fprintf(fp, " ColorMap exch get\n"); fprintf(fp, " } {\n"); fprintf(fp, " ()\n"); fprintf(fp, " } ifelse\n"); fprintf(fp, "} bind false 3 colorimage\n\n"); ptr = Image; for (i=0; i<Height; i++) for (j=0; j<Width; j++,ptr++) { fprintf(fp, "%02x", *ptr); if (count++ > 31) { count = 1; fprintf(fp, "\n"); } } } else { fprintf(fp, "/rgbstr 256 string def\n"); fprintf(fp, "%.1f %.1f translate\n", tx, ty); fprintf(fp, "%.1f %.1f scale\n", sx, sy); if (landscape) fprintf(fp, "90 rotate\n"); fprintf(fp, "%d %d 8\n", Width, Height); fprintf(fp, "[%d 0 0 %d 0 %d]\n", Width, -Height, Height); fprintf(fp, "{ currentfile rgbstr readhexstring pop }\n"); fprintf(fp, "bind false 3 colorimage\n\n"); ptr = Image; for (i=0; i<Height; i++) for (j=0; j<Width; j++,ptr++) { fprintf(fp, "%02x%02x%02x", Red[*ptr], Green[*ptr], Blue[*ptr]); if (count++ > 7) { count = 1; fprintf(fp, "\n"); } } } } else { /* B&W */ fprintf(fp, "/picstr 256 string def\n"); fprintf(fp, "%.1f %.1f translate\n", tx, ty); fprintf(fp, "%.1f %.1f scale\n", sx, sy); if (landscape) fprintf(fp, "90 rotate\n"); fprintf(fp, "%d %d 8\n", Width, Height); fprintf(fp, "[%d 0 0 %d 0 %d]\n", Width, -Height, Height); fprintf(fp, "{ currentfile picstr readhexstring pop }\n"); fprintf(fp, "bind image\n\n"); ptr = Image; for (i=0; i<Height; i++) for (j=0; j<Width; j++,ptr++) { fprintf(fp, "%02x", Red[*ptr]); if (count++ > 31) { count = 1; fprintf(fp, "\n"); } } } fprintf(fp, "\n\nshowpage\n"); } \End\Of\Shar\ else echo "will not over write ./psgifload.c" fi echo "Finished archive 1 of 1" exit
dcj@bar.zk3.dec.com (Dave Jedlinsky OSEM) (03/27/90)
# Eat this, line-eater! Whoops, the shar file I posted was corrupt. That'll teach me to post something without testing to make sure it unarchives correctly. Here is a working version of the gif2ps source code. -David Jedlinsky Digital Equipment Corporation | Hey, I just work here. They don't dcj@decvax.dec.com | have anything to do with these opinions. ...!decwrl!decvax!dcj | -----------------------------Cut Here--------------------------------- #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -s ./AUTHOR` then echo "writing ./AUTHOR" cat > ./AUTHOR << '\End\Of\Shar\' gif2ps converted by David C. Jedlinsky (dcj@decvax.dec.com), based on the program xgif, by John Bradley. -dcj --- Written by John Bradley (bradley@cis.upenn.edu), one of the fine folks at the University of Pennsylvania. Do whatever you want with this program, though it *would* be nice if my name remained on it somewhere... Other than that, it may be freely modified, distributed, and used to fill up disk space. --jhb \End\Of\Shar\ else echo "will not over write ./AUTHOR" fi if `test ! -s ./Makefile` then echo "writing ./Makefile" cat > ./Makefile << '\End\Of\Shar\' # # Makefile for gif2ps # OBJS = \ psgif.o \ psgifload.o SRCS = \ psgif.c \ psgifload.c .c.o: cc -c $*.c gif2ps: ${OBJS} cc -o gif2ps ${OBJS} psgifload.o: psgifload.c psgif.h psgif.o: psgif.c psgif.h clean: rm -rf *.o *~ gif2ps \End\Of\Shar\ else echo "will not over write ./Makefile" fi if `test ! -s ./README` then echo "writing ./README" cat > ./README << '\End\Of\Shar\' gif2ps is a program that converts GIF images to a form that can be printed on an Postscript printer. The images can be freely rescaled. Based (heavily) on xgif, by John Bradley. -David Jedlinsky (dcj@decvax.dec.com) --- A collection of GIF images is available via anonymous ftp to "dsl.cis.upenn.edu" [128.91.2.12]. John Bradley - bradley@cis.upenn.edu --- Revision History: Removed X code, added PostScript output (dcj, 3/20/90). Original version, converted from xgif, patchlevel 2 (2/13/89). \End\Of\Shar\ else echo "will not over write ./README" fi if `test ! -s ./gif2ps.1` then echo "writing ./gif2ps.1" cat > ./gif2ps.1 << '\End\Of\Shar\' .TH gif2ps 1 .SH NAME gif2ps \- converts GIF (*) pictures to PostScript (**) .SH SYNTAX \fBgif2ps\fP [-c] [-i] \fIfilename\fP [-l] [-o \fIfilename\fP] [-s \fIscale\fP] .SH DESCRIPTION \fBgif2ps\fP is a conversion program which converts GIF images to the PostScript language. .SH OPTIONS The '-c' option changes the output to a more space-efficient compact representation. The drawback of this option is that the computation time on the side of the printer is increased by approximately a factor of five. It is most useful on a printer with a fast processor and a slow serial interface, as it reduces the time to actually download the picture. .PP The '-i' option specifies the input filename. The '-i' itself is optional, and is provided only for symmetry with the '-o' option. .PP The '-l' option renders the image in landscape mode (the image is rotated 90 degrees from the orientation specified in the GIF file). .PP The '-o' option specifies the output filename. If \fIfilename\fP is not supplied, the PostScript code will be written to stdout. .PP The '-s' option specifies a scale factor, which must be greater than zero. \fIScale\fP can be a floating point number, the value defaults to 1. .PP If the GIF image consists solely of grayscale colors, the generated PostScript output will use the more efficient \fIimage\fP operator to render the image. .SH LIMITATIONS Standard input is not a valid input file in the current implementation. .PP If the GIF image is not a grayscale image, the printer must support the \fIcolorimage\fP operator. The generated code will cause a printer which does not support the operator to print an error message and quit. .PP This program ignores 'local colormaps' in GIF files (see the GIF spec for details). It also only displays the first image in GIF files that have multiple images in them. .PP .SH AUTHOR David Jedlinsky (dcj@decvax.dec.com), based (heavily) on xgif, by John Bradley (bradley@cis.upenn.edu), which is in turn based (heavily) on gif2ras.c, by Patrick J. Naughton (naughton@wind.sun.com), a program that converts GIF pictures to Sun Rasterfiles. .PP (*) GIF is a no doubt a trademark of CompuServe, so watch it! .PP (**) PostScript is a trademark of Adobe Systems Incorporated. \End\Of\Shar\ else echo "will not over write ./gif2ps.1" fi if `test ! -s ./psgif.c` then echo "writing ./psgif.c" cat > ./psgif.c << '\End\Of\Shar\' /* * gif2ps.c - converts GIF pictures to Postscript. * * Author: David Jedlinsky, Digital Equipment Corporation * (dcj@decvax.dec.com) * John Bradley, University of Pennsylvania * (bradley@cis.upenn.edu) */ #define MAIN #include "psgif.h" /*******************************************/ main(argc, argv) int argc; char *argv[]; /*******************************************/ { int i; char *in, *out; FILE *outp; cmd = argv[0]; in = out = NULL; compact = 0; landscape = 0; scale = 1.0; /*********************Options*********************/ for (i = 1; i < argc; i++) { if (!strncmp(argv[i], "-c", 2)) { /* compact */ compact = 1; continue; } if (!strncmp(argv[i], "-l", 2)) { /* landscape */ landscape = 1; continue; } if (!strncmp(argv[i], "-s", 2)) { /* scale */ if (++i >= argc) Syntax(cmd); if ((scale = atof(argv[i])) == 0.0) { fprintf(stderr, "%s: Invalid scale.\n", cmd); exit(1); } continue; } if (!strncmp(argv[i], "-i", 2)) { /* input */ if (++i >= argc) Syntax(cmd); in = argv[i]; continue; } if (!strncmp(argv[i], "-o", 2)) { /* output */ if (++i >= argc) Syntax(cmd); out = argv[i]; continue; } if (argv[i][0] != '-') { /* the file name */ in = argv[i]; continue; } Syntax(cmd); } if (in == NULL) { /* in="-"; */ Syntax(cmd); } /********** Open File/Write out Header Information********/ if (out==NULL) { out = "stdout"; outp=stdout; } else { outp=fopen(out, "w"); } fprintf(outp, "%%!PS-Adobe-1.0\n"); fprintf(outp, "%%%%Title: %s\n", out); fprintf(outp, "%%%%Creator: %s\n", cmd); fprintf(outp, "%%%%Pages: (atend)\n"); fprintf(outp, "%%%%EndComments\n\n"); /* Add macros needed here. */ fprintf(outp, "%% GIF macros\n\n"); fprintf(outp, "%%%%EndProlog\n\n"); /***************** Open/Read the File *****************/ LoadGIF(in, outp); /*************** Write/Close Output File ***************/ fprintf(outp, "%%%%Trailer\n"); fprintf(outp, "%%%%Pages: 1\n"); if (outp != stdout) if (fclose(outp) == EOF) fprintf(stderr, "%s: error closing %s.\n", cmd, out); } /***********************************/ Syntax(cmd) char *cmd; { fprintf(stderr, "Usage: %s [-c] [-i] infile [-l] [-s scale] [-o outfile]\n",cmd); exit(1); } /***********************************/ FatalError (identifier) char *identifier; { fprintf(stderr, "%s: %s\n",cmd, identifier); exit(-1); } \End\Of\Shar\ else echo "will not over write ./psgif.c" fi if `test ! -s ./psgif.h` then echo "writing ./psgif.h" cat > ./psgif.h << '\End\Of\Shar\' /* * psgif.h - header file for gif2ps, but you probably already knew as much */ #define REVDATE "Rev: 10/26/89" /* include files */ #include <stdio.h> #include <math.h> #include <ctype.h> #ifndef MAIN #define WHERE extern #else #define WHERE #endif typedef unsigned char byte; void *malloc(), exit(), _exit(), free(); /* global vars */ WHERE int numcols,compact,bw,landscape; WHERE unsigned long cols[256]; WHERE char *cmd; WHERE double scale; #ifndef False #define False 0 #define True !False #endif \End\Of\Shar\ else echo "will not over write ./psgif.h" fi if `test ! -s ./psgifload.c` then echo "writing ./psgifload.c" cat > ./psgifload.c << '\End\Of\Shar\' /* * psgifload.c - based strongly on... * * gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun Raster image. * * Copyright (c) 1988, 1989 by Patrick J. Naughton * * Author: Patrick J. Naughton * naughton@wind.sun.com * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * */ #include "psgif.h" typedef int boolean; #define NEXTBYTE (*ptr++) #define IMAGESEP 0x2c #define INTERLACEMASK 0x40 #define COLORMAPMASK 0x80 FILE *fp; int BitOffset = 0, /* Bit Offset of next code */ XC = 0, YC = 0, /* Output X and Y coords of current pixel */ Pass = 0, /* Used by output routine if interlaced pic */ OutCount = 0, /* Decompressor output 'stack count' */ RWidth, RHeight, /* screen dimensions */ Width, Height, /* image dimensions */ LeftOfs, TopOfs, /* image offset */ BitsPerPixel, /* Bits per pixel, read from GIF header */ BytesPerScanline, /* bytes per scanline in output raster */ ColorMapSize, /* number of colors */ Background, /* background color */ CodeSize, /* Code size, read from GIF header */ InitCodeSize, /* Starting code size, used during Clear */ Code, /* Value returned by ReadCode */ MaxCode, /* limiting value for current code size */ ClearCode, /* GIF clear code */ EOFCode, /* GIF end-of-information code */ CurCode, OldCode, InCode, /* Decompressor variables */ FirstFree, /* First free code, generated per GIF spec */ FreeCode, /* Decompressor, next free slot in hash table */ BitMask, /* AND mask for data size */ ReadMask; /* Code AND mask for current code size */ byte FinChar; /* Decompressor variable */ boolean Interlace, HasColormap; boolean Verbose = False; byte *Image; /* The result array */ byte *RawGIF; /* The heap array to hold it, raw */ byte *Raster; /* The raster data stream, unblocked */ /* The hash table used by the decompressor */ int Prefix[4096]; int Suffix[4096]; /* An output array used by the decompressor */ byte OutCode[1025]; /* The color map, read from the GIF header */ byte Red[256], Green[256], Blue[256], used[256]; int numused; char *id = "GIF87a"; /*****************************/ LoadGIF(fname, outp) char *fname; FILE *outp; /*****************************/ { int filesize; register byte ch, ch1; register byte *ptr, *ptr1; register int i; /* stdin won't work with the current implementation! */ /* if (strcmp(fname,"-") == 0) { fp = stdin; fname = "<stdin>"; } else*/ fp = fopen(fname,"r"); if (!fp) FatalError("file not found"); /* find the size of the file */ if (fseek(fp, 0L, 2) != 0) FatalError("couldn't seek to end of file"); filesize = ftell(fp); if (fseek(fp, 0L, 0) != 0) FatalError("couldn't seek to start of file"); if (!(ptr = RawGIF = (byte *) malloc((size_t)filesize))) FatalError("not enough memory to read gif file"); if (!(Raster = (byte *) malloc((size_t)filesize))) FatalError("not enough memory to read gif file"); if (fread((void *)ptr, (size_t)filesize, 1, fp) != (size_t)1) FatalError("GIF data read failed"); if (strncmp((char *)ptr, id, 6)) FatalError("not a GIF file"); ptr += 6; /* Get variables from the GIF screen descriptor */ ch = NEXTBYTE; RWidth = ch + 0x100 * NEXTBYTE; /* screen dimensions... not used. */ ch = NEXTBYTE; RHeight = ch + 0x100 * NEXTBYTE; if (Verbose) fprintf(stderr, "screen dims: %dx%d.\n", RWidth, RHeight); ch = NEXTBYTE; HasColormap = ((ch & COLORMAPMASK) ? True : False); BitsPerPixel = (ch & 7) + 1; numcols = ColorMapSize = 1 << BitsPerPixel; BitMask = ColorMapSize - 1; Background = NEXTBYTE; /* background color... not used. */ if (NEXTBYTE) /* supposed to be NULL */ FatalError("corrupt GIF file (bad screen descriptor)"); /* Read in global colormap. */ if (HasColormap) { if (Verbose) fprintf(stderr, "%s is %dx%d, %d bits per pixel, (%d colors).\n", fname, Width,Height,BitsPerPixel, ColorMapSize); for (i = 0; i < ColorMapSize; i++) { Red[i] = NEXTBYTE; Green[i] = NEXTBYTE; Blue[i] = NEXTBYTE; used[i] = 0; } numused = 0; } else { /* no colormap in GIF file */ fprintf(stderr,"%s: warning! no colortable in this file. Winging it.\n",cmd); if (!numcols) numcols=256; for (i=0; i<numcols; i++) cols[i] = (unsigned long) i; } /* Check for image seperator */ if (NEXTBYTE != IMAGESEP) FatalError("corrupt GIF file (no image separator)"); /* Now read in values from the image descriptor */ ch = NEXTBYTE; LeftOfs = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; TopOfs = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; Width = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; Height = ch + 0x100 * NEXTBYTE; Interlace = ((NEXTBYTE & INTERLACEMASK) ? True : False); if (Verbose) fprintf(stderr, "Reading a %d by %d %sinterlaced image...", Width, Height, (Interlace) ? "" : "non-"); else fprintf(stderr, "%s: %s is %dx%d, %d colors ", cmd, fname, Width,Height,ColorMapSize); /* Note that I ignore the possible existence of a local color map. * I'm told there aren't many files around that use them, and the spec * says it's defined for future use. This could lead to an error * reading some files. */ /* Start reading the raster data. First we get the intial code size * and compute decompressor constant values, based on this code size. */ CodeSize = NEXTBYTE; ClearCode = (1 << CodeSize); EOFCode = ClearCode + 1; FreeCode = FirstFree = ClearCode + 2; /* The GIF spec has it that the code size is the code size used to * compute the above values is the code size given in the file, but the * code size used in compression/decompression is the code size given in * the file plus one. (thus the ++). */ CodeSize++; InitCodeSize = CodeSize; MaxCode = (1 << CodeSize); ReadMask = MaxCode - 1; /* Read the raster data. Here we just transpose it from the GIF array * to the Raster array, turning it from a series of blocks into one long * data stream, which makes life much easier for ReadCode(). */ ptr1 = Raster; do { ch = ch1 = NEXTBYTE; while (ch--) *ptr1++ = NEXTBYTE; if ((ptr1 - Raster) > filesize) { FatalError("corrupt GIF file (unblock)"); } } while(ch1); free((void *)RawGIF); /* We're done with the raw data now... */ if (Verbose) { fprintf(stderr, "done.\n"); fprintf(stderr, "Decompressing..."); } /* Allocate buffer to hold decompressed image */ Image = (byte *)malloc((size_t)Width*Height); if (!Image) FatalError("not enough memory for Image"); BytesPerScanline = Width; /* Decompress the file, continuing until you see the GIF EOF code. * One obvious enhancement is to add checking for corrupt files here. */ Code = ReadCode(); while (Code != EOFCode) { /* Clear code sets everything back to its initial value, then reads the * immediately subsequent code as uncompressed data. */ if (Code == ClearCode) { CodeSize = InitCodeSize; MaxCode = (1 << CodeSize); ReadMask = MaxCode - 1; FreeCode = FirstFree; CurCode = OldCode = Code = ReadCode(); FinChar = CurCode & BitMask; AddToPixel(FinChar); } else { /* If not a clear code, then must be data: save same as CurCode and InCode */ CurCode = InCode = Code; /* If greater or equal to FreeCode, not in the hash table yet; * repeat the last character decoded */ if (CurCode >= FreeCode) { CurCode = OldCode; OutCode[OutCount++] = FinChar; } /* Unless this code is raw data, pursue the chain pointed to by CurCode * through the hash table to its end; each code in the chain puts its * associated output code on the output queue. */ while (CurCode > BitMask) { if (OutCount > 1024) { fprintf(stderr,"\nCorrupt GIF file (OutCount)!\n"); _exit(-1); /* calling 'exit(-1)' dumps core, so I don't */ } OutCode[OutCount++] = Suffix[CurCode]; CurCode = Prefix[CurCode]; } /* The last code in the chain is treated as raw data. */ FinChar = CurCode & BitMask; OutCode[OutCount++] = FinChar; /* Now we put the data out to the Output routine. * It's been stacked LIFO, so deal with it that way... */ for (i = OutCount - 1; i >= 0; i--) AddToPixel(OutCode[i]); OutCount = 0; /* Build the hash table on-the-fly. No table is stored in the file. */ Prefix[FreeCode] = OldCode; Suffix[FreeCode] = FinChar; OldCode = InCode; /* Point to the next slot in the table. If we exceed the current * MaxCode value, increment the code size unless it's already 12. If it * is, do nothing: the next code decompressed better be CLEAR */ FreeCode++; if (FreeCode >= MaxCode) { if (CodeSize < 12) { CodeSize++; MaxCode *= 2; ReadMask = (1 << CodeSize) - 1; } } } Code = ReadCode(); } free((void *)Raster); if (Verbose) fprintf(stderr, "done.\n"); else fprintf(stderr,"(of which %d are used)\n",numused); if (fp != stdin) if (fclose(fp) == EOF) fprintf(stderr, "%s: error closing %s.\n", cmd, fname); ColorDicking(fname); WriteGIFdata(outp); } /* Fetch the next code from the raster data stream. The codes can be * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to * maintain our location in the Raster array as a BIT Offset. We compute * the byte Offset into the raster array by dividing this by 8, pick up * three bytes, compute the bit Offset into our 24-bit chunk, shift to * bring the desired code to the bottom, then mask it off and return it. */ ReadCode() { int RawCode, ByteOffset; ByteOffset = BitOffset / 8; RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]); if (CodeSize >= 8) RawCode += (0x10000 * Raster[ByteOffset + 2]); RawCode >>= (BitOffset % 8); BitOffset += CodeSize; return(RawCode & ReadMask); } AddToPixel(Index) byte Index; { if (YC<Height) *(Image + YC * BytesPerScanline + XC) = Index; if (!used[Index]) { used[Index]=1; numused++; } /* Update the X-coordinate, and if it overflows, update the Y-coordinate */ if (++XC == Width) { /* If a non-interlaced picture, just increment YC to the next scan line. * If it's interlaced, deal with the interlace as described in the GIF * spec. */ XC = 0; if (!Interlace) YC++; else { switch (Pass) { case 0: YC += 8; if (YC >= Height) { Pass++; YC = 4; } break; case 1: YC += 8; if (YC >= Height) { Pass++; YC = 2; } break; case 2: YC += 4; if (YC >= Height) { Pass++; YC = 1; } break; case 3: YC += 2; break; default: break; } } } } /*************************/ ColorDicking(fname) char *fname; { int i; bw = 1; for (i = 0; i < ColorMapSize; i++) if (used[i] && ((Red[i] != Green[i]) || (Red[i] != Blue[i]) || (Green[i] != Blue[i]))) bw = 0; if (bw) fprintf(stderr, "%s: %s is a grayscale image.\n", cmd, fname); } /*************************/ WriteGIFdata(fp) FILE *fp; { int i, j, count = 1; double sx, sy, tx, ty, tmp; byte *ptr = Image; fprintf(fp, "%%%%Page: 1 1\n\n"); sx = (double)Width * scale; sy = (double)Height * scale; if (landscape) { tx = 306.0 + sy / 2; ty = 396.0 - sx / 2; tmp = sx; sx = sy; sy = tmp; } else { tx = 306.0 - sx / 2; ty = 396.0 - sy / 2; } if (!bw) { fprintf(fp, "systemdict /colorimage known\n"); fprintf(fp, "userdict /colorimage known\n"); fprintf(fp, "or not\n"); fprintf(fp, "{\n"); fprintf(fp, " /Times-Roman findfont 20 scalefont setfont\n"); fprintf(fp, " 72 72 moveto\n"); fprintf(fp, " (This picture requires the colorimage operator.) show\n"); fprintf(fp, " showpage stop\n"); fprintf(fp, "} if\n\n"); if (compact) { fprintf(fp, "/ColorMap %d array def\n", ColorMapSize); for (i=0; i<ColorMapSize; i++) { if (i % 8 == 0) fprintf(fp, "\n"); fprintf(fp, "<%02x%02x%02x> ", Red[i], Green[i], Blue[i]); } fprintf(fp, "\nColorMap astore\n\n"); fprintf(fp, "/rgbstr 3 string def\n"); fprintf(fp, "/nextch 1 string def\n"); /* Center the image on a 8.5x11 sheet of paper */ fprintf(fp, "%.1f %.1f translate\n", tx, ty); fprintf(fp, "%.1f %.1f scale\n", sx, sy); if (landscape) fprintf(fp, "90 rotate\n"); fprintf(fp, "%d %d 8\n", Width, Height); fprintf(fp, "[%d 0 0 %d 0 %d]\n", Width, -Height, Height); fprintf(fp, "{\n"); fprintf(fp, " currentfile nextch readhexstring\n"); fprintf(fp, " {\n"); fprintf(fp, " 0 get\n"); fprintf(fp, " ColorMap exch get\n"); fprintf(fp, " } {\n"); fprintf(fp, " ()\n"); fprintf(fp, " } ifelse\n"); fprintf(fp, "} bind false 3 colorimage\n\n"); ptr = Image; for (i=0; i<Height; i++) for (j=0; j<Width; j++,ptr++) { fprintf(fp, "%02x", *ptr); if (count++ > 31) { count = 1; fprintf(fp, "\n"); } } } else { fprintf(fp, "/rgbstr 256 string def\n"); fprintf(fp, "%.1f %.1f translate\n", tx, ty); fprintf(fp, "%.1f %.1f scale\n", sx, sy); if (landscape) fprintf(fp, "90 rotate\n"); fprintf(fp, "%d %d 8\n", Width, Height); fprintf(fp, "[%d 0 0 %d 0 %d]\n", Width, -Height, Height); fprintf(fp, "{ currentfile rgbstr readhexstring pop }\n"); fprintf(fp, "bind false 3 colorimage\n\n"); ptr = Image; for (i=0; i<Height; i++) for (j=0; j<Width; j++,ptr++) { fprintf(fp, "%02x%02x%02x", Red[*ptr], Green[*ptr], Blue[*ptr]); if (count++ > 7) { count = 1; fprintf(fp, "\n"); } } } } else { /* B&W */ fprintf(fp, "/picstr 256 string def\n"); fprintf(fp, "%.1f %.1f translate\n", tx, ty); fprintf(fp, "%.1f %.1f scale\n", sx, sy); if (landscape) fprintf(fp, "90 rotate\n"); fprintf(fp, "%d %d 8\n", Width, Height); fprintf(fp, "[%d 0 0 %d 0 %d]\n", Width, -Height, Height); fprintf(fp, "{ currentfile picstr readhexstring pop }\n"); fprintf(fp, "bind image\n\n"); ptr = Image; for (i=0; i<Height; i++) for (j=0; j<Width; j++,ptr++) { fprintf(fp, "%02x", Red[*ptr]); if (count++ > 31) { count = 1; fprintf(fp, "\n"); } } } fprintf(fp, "\n\nshowpage\n"); } \End\Of\Shar\ else echo "will not over write ./psgifload.c" fi echo "Finished archive 1 of 1" exit
pjb@tcom.stc.co.uk (Peter J. Bishop) (07/11/90)
Is there someone out there who could mail me the source of gif2ps. I cannot find it on any of the archive servers to which I have access and I am unable to connect to any other sites via ftp. Thanks in advance, -- Peter Bishop. <pjb@tcom.stc.co.uk> || ...!mcvax!ukc!stc!pjb STC TSD-E, Engineering Services, 20-22 Edinburgh Way, Harlow. Essex CM20 2DE Phone : +44 279 626626 x2795
jstewart@rodan.acs.syr.edu (Ace Stewart) (01/11/91)
I haven't had a chance to work with this yet, so I am asking a blind question. Does anyone know whether or not a color GIF file, using gif2ps will convert to color PS? Before I go spending money on such things, I would like to know whether or not anyone has experience in this... Cheers and thanx..Ace -- | Ace Stewart (Jonathan III) |A /\ | | Affiliation: Eastman Kodak Company. Rochester New York | _/ \_ | | Internet/ARPA: jstewart@rodan.acs.syr.edu | \_ _/ | | Bitnet: jstewart@sunrise.bitnet | /\ A|