/*
 *      TextPS.c
 *
 *      The AthenaTools Plotter Widget Set - Version 6.0
 *
 *      klin, Tue Jul  7 13:59:47 1992
 *      klin, Sat Aug 15 10:31:50 1992, patchlevel 4
 *                                      Changed <At/..> to <X11/At/..>.
 */
static char SCCSid[] = "@(#) Plotter V6.0  92/08/15  TextPS.c";

/*

Copyright 1990,1991 by the Massachusetts Institute of Technology

All rights reserved.

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, and that the name of the firms, institutes
or employers of the authors not be used in advertising or publicity
pertaining to distribution of the software without specific, written
prior permission.

THE AUTHORS AND THEIR FIRMS, INSTITUTES OR EMPLOYERS DISCLAIM ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE AUTHORS AND THEIR FIRMS,
INSTITUTES OR EMPLOYERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

*/

#include <X11/At/Text.h>
#include <X11/At/FontFamilyP.h>

#define ENVIRONMENT 1
#define SYMBOL      2
#define NEWLINE     3
#define END         4
#define STRING      5
#define END_OF_STRING 6
#define START_OF_STRING 7


#define    env_bold             1       /* 'b' */
#define    env_ita              2       /* 'i' */
#define    env_greek            3       /* 'g' */
#define    env_roman            4       /* 'r' - change to roman font */
#define    env_bigger           5
#define    env_smaller          6
#define    env_super            7       /* '+' */
#define    env_sub              8       /* '-' */

/*
 * on entry, t points at at the beginning of an env.
 * this procedure calculate a bounding box for that env, recursing
 * where necessary.  It puts the metrics in the text token.
 * returns the first text token following the end of the environment
 * so after a recursive call the parent call can continue from the
 * right place.
 */
static AtTextPSFormat *compute_env_metrics(f)
AtTextPSFormat *f;
{
    AtTextPSFormat *fmt, *next;
    short width = 0, ascent = 0, descent = 0;

    fmt = f->next;
    while ((fmt != NULL) && (fmt->width != -2)) {
	if (fmt->width == 0) {
	    fmt = fmt->next;
	    continue;
	}

	if (fmt->width == -1) {
	    next = compute_env_metrics(fmt);
	    width += fmt->width;
	    if (f->baseline - fmt->baseline + fmt->ascent >  ascent)
		ascent = f->baseline - fmt->baseline + fmt->ascent;
	    if (fmt->baseline - f->baseline + fmt->descent > descent)
		descent = fmt->baseline - f->baseline + fmt->descent;
	    fmt = next;
	    continue;
	}

	width += fmt->width;
	if (fmt->ascent > ascent) ascent = fmt->ascent;
	if (fmt->descent > descent) descent = fmt->descent;
	fmt = fmt->next;
    }
    f->width = width;
    f->ascent = ascent;
    f->descent = descent;

    if ((fmt != NULL) && (fmt->width == -2))
      fmt = fmt->next;
    return fmt;
}

static void psformat P((AtText *t));
static void psformat(t)
AtText *t;
{
    int numnodes, i;
    AtTextToken *tok;
    AtTextPSFormat *fmt;
    AtFontFamily *family = t->family;
    AtFontFamily *symbolfamily;
    Display *dpy;
    char symstr[2];
    int face = t->style;
    int size = t->size;
    int base = 0;
    AtFontFamily *familystack[50];
    int facestack[50];
    int sizestack[50];
    int basestack[50];
    int sp = 0;
#define pushenv()  familystack[sp] = family;\
		   facestack[sp] = face;\
		   sizestack[sp] = size;\
		   basestack[sp] = base;\
		   sp++

#define popenv()   sp--;\
		   family = familystack[sp];\
		   face = facestack[sp];\
		   size = sizestack[sp];\
		   base = basestack[sp]




    if (t->psformat == NULL) { /* get space to format it */
	numnodes = 0;
	for(tok = t->parsed; tok->type != END_OF_STRING; tok = tok->next)
	    numnodes++;
	t->psformat = (AtTextPSFormat*)calloc(numnodes,sizeof(AtTextPSFormat));
	for(i=0; i<numnodes-1; i++) t->psformat[i].next = &t->psformat[i+1];
    }

    dpy = AtFontFamilyGetDisplay(family);
    symbolfamily = AtFontFamilyGet(dpy,"symbol");

    tok = t->parsed;
    fmt = t->psformat;
    while (tok->type != END_OF_STRING) {
	fmt->size = size;
	switch (tok->type) {
	case NEWLINE:
	    break;
	case STRING:
	    fmt->font = INFO(family)->psnames[face];
	    fmt->ascent = AtFontPSAscent(family, face, size);
	    fmt->descent = AtFontPSDescent(family, face, size);
	    fmt->width=AtFontPSTextWidth(family,face,size,tok->str,tok->code);
	    fmt->baseline = base;
	    break;
	case SYMBOL:
	    fmt->font = INFO(symbolfamily)->psnames[0];
	    fmt->ascent = AtFontPSAscent(symbolfamily, 0, size);
	    fmt->descent = AtFontPSDescent(symbolfamily, 0, size);
	    symstr[0] = (char)tok->code;
	    symstr[1] = '\0';
	    fmt->width = AtFontPSTextWidth(family, face, size, symstr, 1);
	    fmt->baseline = base;
	    break;
	case ENVIRONMENT:
	    pushenv();
	    fmt->baseline = base;
	    fmt->width = -1; /* special code for compute_env_metrics */
	    switch (tok->code) {
	    case env_bold:
		face |= AtFontBOLD;
		break;
	    case env_ita:
		face |= AtFontITALIC;
		break;
	    case env_greek:
		family = symbolfamily;
		break;
	    case env_roman:
		family = t->family;
		face = AtFontPLAIN;
		break;
	    case env_bigger:
		size = AtFontBigger(size);
		break;
	    case env_smaller:
		size = AtFontSmaller(size);
		break;
	    case env_super:
		size = AtFontSmaller(size);
		base = base+AtFontPixelSize(t->family,size)/2;
		fmt->baseline = base;
		break;
	    case env_sub:
		size = AtFontSmaller(size);
		base = base-AtFontPixelSize(t->family,size)/2;
		fmt->baseline = base;
		break;
	    }
	    break;
	case END:
	    popenv();
	    fmt->width = -2;  /* another special code */
	    break;
	}
	tok = tok->next;
	fmt = fmt->next;
    }
    compute_env_metrics(t->psformat);
}

/* quote close parens in a string,
   insert newlines to prevent any line from getting too long
*/
static void fixup P((char *from, char *to, int len));
static void fixup(from, to, len)
char *from, *to;
int len;
{
    int i,j;

    for(i=j=0; i < len; i++, j++) {
	if ((from[i] == ')') || (from[i] == '('))  to[j++] = '\\';
	to[j] = from[i];
	if ((i+1)%72 == 0) to[j++] = '\n';
    }
    to[j] = '\0';
}


static void psdraw P((FILE *f, AtText *t, int x, int y));
static void psdraw(f, t, x, y)
FILE *f;
AtText *t;
int x, y;
{
    AtTextToken *tok;
    AtTextPSFormat *fmt;
    char buf[500];

    fprintf(f, "GS ");

    if (t->rotated) {
	int tmp;
	tmp = x;
	x = y;
	y = -tmp - t->psformat->ascent;
	fprintf(f, "90 rotate ");
    }

    tok = t->parsed;
    fmt = t->psformat;

    while(tok->type != END_OF_STRING) {
	switch (tok->type) {
	case SYMBOL:
	    fprintf(f, "%d /%s F (\\%o) %d %d S\n",
		    AtFontPointSize(t->family,fmt->size),
		    fmt->font, tok->code,
		    x, y+fmt->baseline);
	    x += fmt->width;
	    break;

	case STRING:
	    fixup(tok->str, buf, tok->code);
	    fprintf(f, "%d /%s F (%s) %d %d S ",
		    AtFontPointSize(t->family, fmt->size),
		    fmt->font, buf,
		    x, y+fmt->baseline);
	    x += fmt->width;
	    break;

	case START_OF_STRING:
	case ENVIRONMENT:
	case NEWLINE:
	case END:
	    break;
	}
	tok = tok->next;
	fmt = fmt->next;
    }
    fprintf(f, "GR\n");
}


int AtTextPSWidth(t)
AtText *t;
{
    if (t->psformat == NULL)
	psformat(t);
    if (t->rotated) return t->psformat->ascent + t->psformat->descent;
    else return t->psformat->width;
}

int AtTextPSAscent(t)
AtText *t;
{
    if (t->psformat == NULL)
	psformat(t);
    if (t->rotated) return t->psformat->width;
    else return t->psformat->ascent;
}

int AtTextPSDescent(t)
AtText *t;
{
    if (t->psformat == NULL)
	psformat(t);
    if (t->rotated) return 0;
    else return t->psformat->descent;
}


void AtTextPSDraw(f, t, x, y)
FILE *f;
AtText *t;
int x, y;
{
  if (t != NULL)  {
    if (t->psformat == NULL)
      psformat(t);
    psdraw(f, t, x, y);
  }
}

void AtTextWritePostscriptProlog(f)
FILE *f;
{
  static char prolog[] =
    "/F { findfont exch scalefont setfont } bind def\n\
     /S { moveto show } bind def\n";

    fprintf(f, prolog);
    *SCCSid = *SCCSid;        /* Keep gcc quiet */
}
