/*
  text-png.c

  by Michael J. Fromberger <http://www.dartmouth.edu/~sting/>
  Copyright (C) 2002 The Trustees of Dartmouth College

  Convert a one-line text message into a small human-readable PNG file
  suitable for use on a web page.

  Usage:
    text-png [options] <message>

  Options:
    --output=file    Write output to this file (default "textN.png")
    --fgcolor=color  Set foreground (text) colour to this value (df. black)
    --bgcolor=color  Set background colour to this value (df. white)
    --serif          Use a serif font (df. no)
    --size=sz        Use this point size (df. 12pt)

  $Id: text-png.c,v 1.3 2004/08/16 14:46:37 sting Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>

#include "gd.h"

#include <unistd.h>

#ifdef HAS_GNU_GETOPT
#include <getopt.h>
struct option g_opts[] = {
  { "output",  1, 0, 'o' },
  { "fgcolor", 1, 0, 'f' }, 
  { "bgcolor", 1, 0, 'b' },
  { "serif",   0, 0, 's' },
  { "size",    1, 0, 'z' }, 
  { NULL,      0, 0, 0 }
};
#endif

char  *g_optstr = "o:f:b:sz:"; /* short option equivalents */

char  *g_serif = "/usr/X11R6/lib/X11/fonts/TTF/VeraSe.ttf";
char  *g_sans  = "/usr/X11R6/lib/X11/fonts/TTF/Vera.ttf";

double g_size = 12.0;   /* default is 12.0 points */
char  *g_font;

FILE *open_file(char *name, char *mode);
int   parse_color(char *str, int *r, int *g, int *b);
int   hex_val(char c);

int main(int argc, char *argv[])
{
  gdImagePtr   image;
  int          bgcolor, fgcolor;
  int          bounds[8]; /* will tell us how big the image needs be */
  int          wid, hgt, x, y, opt;
  int          fgR, fgG, fgB, bgR, bgG, bgB;
  char        *res;       /* result from FreeType */
  FILE        *ofp = NULL;

  g_font = g_sans; /* default font is sans-serif */

  /* Set default colours to black and white */
  fgR = fgG = fgB = 0;
  bgR = bgG = bgB = 255;

  /* Process command-line options, if any */
#ifdef HAS_GNU_GETOPT
  while((opt = getopt_long_only(argc, argv, g_optstr, 
				g_opts, NULL)) >= 0) {
#else
  while((opt = getopt(argc, argv, g_optstr)) >= 0) {
#endif

    switch(opt) {
    case 'o': /* send output to specified file */
      if((ofp = open_file(optarg, "wb")) == NULL) {
	fprintf(stderr, "%s: opening '%s': %s\n",
		argv[0], optarg, strerror(errno));
	return 1;
      }
      break;
    case 'f': /* set foreground colour */
      if(!parse_color(optarg, &fgR, &fgG, &fgB)) {
	fprintf(stderr, "%s: invalid foreground color: %s\n", 
		argv[0], optarg);
	return 1;
      }
      break;
    case 'b': /* set background colour */
      if(!parse_color(optarg, &bgR, &bgG, &bgB)) {
	fprintf(stderr, "%s: invalid background color: %s\n", 
		argv[0], optarg);
	return 1;
      }
      break;
    case 's': /* use a serif font for output */
      g_font = g_serif;
      break;
    case 'z': /* set point size */
      if((g_size = atof(optarg)) <= 0.0) {
	fprintf(stderr, "%s: invalid point size specified\n", argv[0]);
	return 1;
      }
      break;
    default:
#ifdef HAS_GNU_GETOPT
      fprintf(stderr, 
	      "Usage: %s [options] <message>\n"
	      "Options:\n"
	      " --output=file    : specify output file\n"
	      " --fgcolor=color  : specify text colour\n"
	      " --bgcolor=color  : specify background colour\n"
	      " --serif          : use a serif font\n"
	      " --size=pts       : set font size in points\n\n", argv[0]);
#else
      fprintf(stderr, 
	      "Usage: %s [options] <message>\n"
	      "Options:\n"
	      " -o file          : specify output file\n"
	      " -f color         : specify text colour\n"
	      " -b color         : specify background colour\n"
	      " -s               : use a serif font\n"
	      " -z pts           : set font size in points\n\n", argv[0]);
#endif
      return 1;
    }
  }

  /* You have to provide a message */
  if(optind >= argc) {
    fprintf(stderr, "Usage: %s [options] <message>\n", argv[0]);
    return 1;
  }
 
  /* Compute the image bounds needed to draw the string */
  if((res = gdImageStringFT(NULL, bounds, 0, g_font, g_size, 0.0, 
			    0, 0, argv[optind])) != NULL) {
    fprintf(stderr, "%s: error bounding message: %s\n", argv[0], res);
    return 1;
  }

  wid = bounds[2] - bounds[6] + 6;
  hgt = bounds[3] - bounds[7] + 6;
  image = gdImageCreate(wid, hgt);

  bgcolor = gdImageColorResolve(image, bgR, bgG, bgB);
  fgcolor = gdImageColorResolve(image, fgR, fgG, fgB);

  /* Compute starting position for text drawing */
  x = 3 - bounds[6];
  y = 3 - bounds[7];

  /* Draw the actual image this time (should go fast, since GD caches
     the rendering that was done the first time) */
  if((res = gdImageStringFT(image, bounds, fgcolor, g_font, g_size, 0.0,
			    x, y, argv[optind])) != NULL) {
    fprintf(stderr, "%s: error generating image: %s\n", argv[0], res);
    return 1;
  }

  /* If the user didn't specify an output file, make one up */
  if(ofp == NULL) {
    if((ofp = open_file(NULL, "wb")) == NULL) {
      fprintf(stderr, "%s: unable to open output file, sorry\n", argv[0]);
      return 1;
    }
  }

  /* Write out the results as a PNG file */
  gdImagePng(image, ofp);
  fclose(ofp);

  /* Clean up... */
  gdImageDestroy(image);

  return 0;
}

/* {{{ open_file(name, mode) */

FILE *open_file(char *name, char *mode)
{
  static char fnbuf[64];

  if(name == NULL) {
    FILE *res;
    int try;

    for(try = 1; try < 100; ++try) {
      sprintf(fnbuf, "text%d.png", try);

      if((res = fopen(fnbuf, "r")) == NULL)
	return fopen(fnbuf, mode);

      fclose(res);
    }

    return NULL; /* no name found */
  } 

  return fopen(name, mode);
}

/* }}} */

/* {{{ parse_color(str, r, g, b) */

int   parse_color(char *str, int *r, int *g, int *b)
{
  int ix;
  
  if(strlen(str) != 6) 
    return 0;
  
  for(ix = 0; ix < 6; ++ix) {
    if(!isxdigit((int)str[ix]))
      return 0;
  }

  *r = hex_val(str[0]) * 16 + hex_val(str[1]);
  *g = hex_val(str[2]) * 16 + hex_val(str[3]);
  *b = hex_val(str[4]) * 16 + hex_val(str[5]);

  return 1;
}

/* }}} */

/* {{{ hex_val(c) */

int hex_val(char c)
{
  if(isdigit((int)c))
    return c - '0';
  else
    return tolower((int)c) - 'a' + 10;
}

/* }}} */
