/*****************************************************************
 * pbm2ps.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
 *
 * Copyright (C) 1989,1990 by Michael Mauldin.  Permission is granted
 * to use this file in whole or in part for any purpose, educational,
 * recreational or commercial, provided that this copyright notice
 * is retained unchanged.  This software is available to all free of
 * charge by anonymous FTP and in the UUNET archives.
 *
 * pbm2ps.c: 
 *
 * USAGE
 *	% pbm2ps [ flags ] arguments
 *
 * BUGS
 *	Will blow up if the title has PostScript special characters
 *	in it (especially unbalanced parens)
 *
 * EDITLOG
 *	LastEditDate = Sun Jun 24 23:55:37 1990 - Michael Mauldin
 *	LastFileName = /usr2/mlm/src/misc/fbm/pbm2ps.c
 *
 * HISTORY
 * 25-Jun-90  Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon
 *	Package for Release 1.0
 *
 * 16-Jun-89  Michael Mauldin (mlm) at Carnegie Mellon University
 *	Beta release (version 0.95) mlm@cs.cmu.edu
 *
 * 14-Sep-88  Michael Mauldin (mlm) at Carnegie-Mellon University
 *	Created.
 *****************************************************************/

# include <stdio.h>
# include <ctype.h>

# define MAXBAD 10

# define USAGE "pbm2ps [ -s ] [ scale ] < pbm > postscript"

unsigned char	*bits;
int		 w, h;
char		 title[80];

/****************************************************************
 * main: Read a pbm format file and write it out as PostScript
 ****************************************************************/

#ifndef lint
static char *fbmid =
"$FBM pbm2ps.c <1.0> 25-Jun-90  (C) 1989,1990 by Michael Mauldin, source \
code available free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif

main (argc, argv)
char *argv[];
{ int scale = -1, scribe = 0;

  /* Get option */
  while (--argc > 0 && (*++argv)[0] == '-')
  { while (*++(*argv))
    { switch (**argv)
      { case 's':	scribe++; break;
	default:	fprintf (stderr, "Usage: %s\n", USAGE);
			exit (1);
      }
    }
  }

  if (argc > 0 && (scale = atoi (argv[0])) < 1)
  { fprintf (stderr, USAGE); exit (1); }

  if (read_pbm (stdin) && write_ps (scale, scribe)) exit (0);

  exit (1);
}

/****************************************************************
 * read_pbm: Read a pbm bitmap into character array 'bits', setting
 *	     width, height, and title
 ****************************************************************/

read_pbm (rfile)
FILE *rfile;
{ int ch;
  register unsigned char *bmp, *tail;
  int badcnt=0;

  if ((ch = getc (rfile)) != 'P' || (ch = getc (rfile)) != '1')
  { fprintf (stderr, "bad magic number, input not PBM file\n");
    return (0);
  }

  title[0] = '\0';

  if ((w = pbm_getint (stdin)) < 0 || (h = pbm_getint (stdin)) < 0)
  { return (0); }

  bits = (unsigned char *) malloc (w*h);
  
  bmp = bits;
  tail = &bmp[h*w];
  
  /* Read bits, inverting so that 1=white and 0=black */
  while (bmp < tail && (ch = getc (rfile)) != EOF)
  { if (ch == '0') *bmp++ = 1;
    else if (ch == '1') *bmp++ = 0;
    else if (ch == '#') eatcomment ();
    else if (isspace (ch)) /* ignore it */ ;
    else if (++badcnt < MAXBAD)
    { fprintf (stderr, "Ignoring junk character '%c'\n", ch); }
    else
    { fprintf (stderr, "Too many junk characters, bye!\n"); exit (1); }
  }

  if (ch == EOF)
  { fprintf (stderr, "premature EOF, read %d of %d bits in [%dx%d]\n",
		(bmp - bits), (tail - bits), w, h);
    return (0);
  }
  
  return (1);
}

/*****************************************************************
 * pbm_getint: Read a number from a PBM file, ignoring comments
 *****************************************************************/

pbm_getint (rfile)
FILE *rfile;
{ register int ch;
  register int val = 0;

  while ((ch = getc (rfile)) != EOF)
  { if (ch == '#') eatcomment ();
    else if (isspace (ch)) /* ignore it */ ;
    else if (isdigit (ch)) break;
    else
    { fprintf (stderr, "Found junk character '%c' in header\n", ch);
      return (-1);
    }
  }

  while (isdigit (ch))
  { val = val*10 + ch - '0';
    ch = getc (rfile);
  }
  
  return (val);
}

/*****************************************************************
 * eatcomment: Read comments and look for titles
 *****************************************************************/

eatcomment ()
{ char cmtbuf[80];
  register char *s;

  /* Read rest of line, remove trailing newline and skip over leading spaces */
  fgets (cmtbuf, sizeof (cmtbuf), stdin);
  cmtbuf[strlen (cmtbuf) - 1] = '\0';
  for (s=cmtbuf; isspace (*s); s++) ;

  /* If the comment contains the title, squirrel it away */
  if (!strncmp (s, "Title: ", 7)) strcpy (title, s+7);
  fprintf (stderr, "Reading '%s'\n", title);
}

/****************************************************************
 * write_ps: Write out a 1 bit deep bitmap as a PostScript file
 *
 * Output is centered with at least 1" left margin, 1/2" right,
 * top and bottom margins.
 *
 * The title is printed in 14 pt Times-Bold centered at the top.
 * One half inch at the top is reserved for the title
 ****************************************************************/

# define BYTESPERLINE 32
# define PSRES 300	/* printer resolution, dots per inch */
# define PPINCH 72	/* Points per inch */
# define PAGW 8.5	/* page width 8.5 inches */
# define PAGH 11.0	/* page height 11 inches */
# define MAXW 7.0	/* maximum image width 7 inches */
# define MAXH 9.5	/* maximum image height 9.5 inches */
# define LMRG 1.0	/* left margin 1 inche */
# define BMRG 0.5	/* bottom margin 1/2 inche */
# define TMRG 0.125	/* Title margin, 1/8 inch */
# define FSIZ 14	/* Font size for title (before scaling to 300 dpi) */

write_ps (scale, scribe)
int scale, scribe;
{ register int x, y, k, byte, bytes=0;
  register unsigned char *bmp = bits;
  int dotsx, dotsy;
  double pwidth, pheight, ctrx, ctry;

  /* Pick the largest scale factor that makes the image fit */
  if (scale < 1)
  { dotsx = (int) MAXW * PSRES / w;
    dotsy = (int) MAXH * PSRES / h;
    scale = (dotsx < dotsy) ? dotsx : dotsy;
    if (scale < 1) scale = 1;
  }

  fprintf (stderr, "pbm2ps: scale %d\n", scale);

  /* Determine width and height of output in inches */  
  pwidth = (double) w * scale;
  pheight = (double) h * scale;
  ctrx = ((double) MAXW / 2.0 + LMRG) * PSRES;
  ctry = ((double) MAXH / 2.0 + BMRG) * PSRES;

  printf ("%%%! %s\n\n", title[0] ? title : "PBM to PostScript");
  printf ("%lg %lg scale\n", (double) PPINCH / PSRES, (double) PPINCH / PSRES);
  if (title[0])
  { printf ("/centershow { dup stringwidth pop");
    printf (" 2 div 0 exch sub 0 rmoveto show } def\n");
    printf ("/Times-Bold findfont %lg scalefont setfont\n",
	    (double) FSIZ * PSRES / PPINCH);
    printf ("%lg %lg moveto\n",
		ctrx, ctry + pheight / 2.0 + TMRG*PSRES);
    printf ("(%s) centershow\n\n", title);
  }

  printf ("/picstr 32 string def\n");

  if (!scribe)
  { printf ("%lg %lg translate\n", ctrx - pwidth / 2, ctry - pheight / 2); }
  printf ("%lg %lg scale\n", pwidth, pheight);
  printf ("%d %d 1 [ %d 0 0 -%d 0 %d ] ", w, h, w, h, h);
  printf ("{ currentfile picstr readhexstring pop }\n");
  printf ("image\n");

  for (y=0; y<h; y++)
  { for (x=0; x<w; x += 8)
    { byte = 0;
      for (k=0; k<8; k++)
      { byte = (byte << 1) | (((x+k) < w) ? *bmp++ : 0); }

      printf ("%02x", byte);
      if (++bytes % BYTESPERLINE == 0) printf ("\n");
    }
  }
  
  /* Pad so there are exactly BYTESPERLINE bytes in each line */
  if (bytes % BYTESPERLINE)
  { while (bytes++ % BYTESPERLINE) printf ("00");
    printf ("\n");
  }

  if (!scribe) printf ("showpage\n");

  return (1);  
}
