/* $Header: /afs/athena.mit.edu/astaff/project/atdev/src/text/RCS/TextPS.c,v 3.1 91/01/03 17:54:54 crcraig Exp $ */

/*******************************************************************
  Copyright (C) 1990 by the Massachusetts Institute of Technology

   Export of this software from the United States of America is assumed
   to require a specific license from the United States Government.
   It is the responsibility of any person or organization contemplating
   export to obtain such a license before exporting.

WITHIN THAT CONSTRAINT, 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 M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.  M.I.T. makes no representations about the suitability of
this software for any purpose.  It is provided "as is" without express
or implied warranty.

***************************************************************** */

#include <stdio.h>

#ifdef _AtDevelopment_
#include "Text.h"
#include "FontFamilyP.h"    
#else
#include <At/Text.h>
#include <At/FontFamilyP.h>
#endif

#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(AtText *t)
{
    int numnodes, i;
    AtTextToken *tok;
    AtTextPSFormat *fmt;
    AtFontFamily *family = t->family;
    AtFontFamily *symbolfamily;
    Display *dpy;
    char symstr[2];
    int face = AtFontPLAIN;
    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(char *from, char *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(FILE *f, AtText *t, int x, int y)
{
    AtTextToken *tok;
    AtTextPSFormat *fmt;
    char buf[500];

    fprintf(f, "%%%% BeginObject: AtText\n");
    fprintf(f, "gsave\n");

    if (t->rotated) {
	int tmp;
	tmp = x;
	x = y;
	y = -tmp - t->psformat->ascent;
	fprintf(f, "90 rotate\n");
    }
    
    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\n",
		    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, "grestore\n");
    fprintf(f, "%%%% EndObject\n");
}


int AtTextPSWidth(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(AtText *t)
{
    if (t->psformat == NULL)
	psformat(t);
    if (t->rotated) return t->psformat->width;
    else return t->psformat->ascent;
}

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


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

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

    fprintf(f, prolog);
}
