#define BOOLEAN int
#define FALSE 0
#define TRUE 1

#define MAXLINE 512

#define BOT 0
#define TOP 1

#define NORMAL 0
#define UNKNOWN 1
#define BOLD 2
#define ITALIC 3

#define MATH 1
#define MANUAL 2

#define LEFT 0
#define CENTRAL 1
#define RIGHT 2

#ifndef MIN
#define MIN(a,b) ((a)<=(b)?(a):(b))
#endif
#ifndef MAX
#define MAX(a,b) ((b)<=(a)?(a):(b))
#endif

#include <stdio.h>
#include <string.h>
#include <math.h>
#include "dvips.h"
#include "dvips_math.h"
#include "symbol.h"
#include "ISOLatin1.h"
#include "ascii.h"

void crash(char *msg)
{
	fputs(msg, stderr);
	fflush(stderr);
	abort();    
}

/* Character encoding.  Each element of the QE directive produced by
   filter.ps is either an index in the StandardGlyph array, or is
   "NonstandardGlyph" (indicating the corresponding entry in the font's
   encoding specifies some nonstandard glyph). */

typedef unsigned GlyphIndex;
#define NonstandardGlyph 9999

#define UnknownChar '#' /* substitute for nonstandard glyph */

#define FIRSTDvipsGlyphs 0
#define LASTDvipsGlyphs 255
#define FIRSTOldDvipsGlyphs 256
#define LASTOldDvipsGlyphs 384

#define FIRSTTTSpecialGlyphs (FirstTT1+130)
#define LASTTTSpecialGlyphs (FirstTT1+159)
static char *TTSpecialGlyphs[] = {
    "'",     /* quotesinglbase */
    "f",     /* florin */
    "''",    /* quotdblbase */
    "...",   /* ellipsis */
    "+",     /* dagger */
    "#",     /* daggerdbl */
    "\223",  /* circumflex */
    "o/oo",  /* perthousand */
    "S\237", /* Scaron */
    "<",     /* guilsinglleft */
    "OE",    /* OE */
    "#",     /* <undefined> */
    "#",     /* <undefined> */
    "#",     /* <undefined> */
    "#",     /* <undefined> */
    "`",     /* ISOLatin1: quoteleft */
    "'",     /* ISOLatin1: quoteright */
    "``",    /* quotedblleft */
    "''",    /* quotedblright */
    ".",     /* bullet */
    "--",    /* endash */
    "---",   /* emdash */
    "~",     /* ISOLatin1: tilde */
    "^TM",   /* trademark */
    "s\237", /* scaron */
    ">",     /* guilsinglright */
    "oe",    /* oe */
    "#",     /* <undefined> */
    "#",     /* <undefined> */
    "Y\250"  /* Ydieresis" */
  };

/* Declarations of external variables */
extern FILE *ErrorTbl;
extern FILE *out;
extern int FS;
extern int BBox_Left, BBox_Bot,
	   BBox_Right, BBox_Top;
extern int Debug;
extern int MyDebug;
extern char BaseName[100];
extern char *HTMLName;
extern char *ManualDvipsGlyphs[256];

/* Variables concerning options */
extern BOOLEAN PsImages;	/* If images have also PS format	 */
extern BOOLEAN LnBreaks;	/* Output <BR> for each \n or not	 */
extern int PagesPlus;		/* Add this to Page Index		 */
extern int MinDist;		/* Min distance between two lines	 */
				/* in order to specify larger breaks	 */
extern int Diff;		/* Difference between one line's	 */
				/* beginning and ending			 */
extern int Align;		/* Line alignment			 */
extern int EnCoding;		/* How to deal with dvips noname fonts	 */

/* Struct holding info about images */
struct ImageNode
{
	int Page;
	int ImageNum;
	int CropTop, CropBot,
		CropLeft, CropRight;
	struct ImageNode *next;
	struct ImageNode *prev;
};

struct ImageNode *ImageHead;
struct ImageNode *ImageTail;

#define VERTIC 0
#define HORIZ 1
#define POINT 2

/* Struct holding info about lines */
struct LineNode
{
	int LineX;
	int LineY;
	int LineW;
	int LineH;
	int VorH;
	struct LineNode *next;
};

struct LineNode *LineHead;

int FootStar=0;
int FootParsed=0;
BOOLEAN FootNotes=FALSE;

BOOLEAN ForceOutput=FALSE;
BOOLEAN EmptyLine=TRUE;

BOOLEAN TableOfCon=FALSE;
int Dots=0;

BOOLEAN OListsOpen=0;
BOOLEAN UListsOpen=0;
int UList_start=0;
int OList_start=0;

int FontWeight=UNKNOWN;
int Page=1;
int ErrorNum=1;
int ImageNum=1;
int ImageInPage=1;
BOOLEAN UnDone=FALSE;
char PaperSize[30];

typedef struct
{
	double blx, bly, trx, try; /* font matrix in character coordinates */
	struct {double x, y;} chr[256]; /* widths in character coordinates */
} MetricsRec;
typedef MetricsRec *Metrics;
typedef Metrics MetricsTable[];

typedef GlyphIndex EncodingVector[256];
typedef EncodingVector *Encoding;
typedef Encoding EncodingTable[];

typedef struct
{
	double x, y; /* (1000,0) in font's character coordinate system */
	double xp, yp; /* (0,1000) in font's character coordinate system */
	double bx, by, tx, ty; /* height of font bbox in reporting coords */
	int m; /* index in "metrics" */
	int e; /* index in "encodings" */
	int w; /* font's weight (Normal, Bold, Italic) */
	char name[50]; /* Actual font's name */
	int Html_FontSize; /* Font size adjusted to the final html format */
} FontRec;
typedef FontRec *Font;
typedef Font FontTable[];


/* State of the instance "t". */
static struct
{
	FILE *rd; /* the input stream */
	double scale; /* conversion from reporting coordinates to pixels */
	int metricsSize;
	MetricsTable *metrics;
	int encodingSize;
	EncodingTable *encoding;
	int fontSize;
	FontTable *font;

	/* Data for current word prefix: */
	char buf[1000];
	int lbuf; /* elements 0 through "lbuf-1" of "buf" are in use */
	int f; /* font number */
	int Prev_FontSize;
	char Prev_FontName[50];
	double x0, y0, x1, y1; /* initial and final currentpoint */

	BOOLEAN nonEmptyPage;
	int blx, bly, trx, try; /* bounding box of last word output */
} t;

/* Information about any table occurance */
static struct
{
	BOOLEAN Opened;	/* Informs us whether a table is open */
	int LeftX, RightX, UpperY, LowerY; /* BBox of the table */
} table;

/* Information about any shape occurance */
static struct
{
	BOOLEAN Found;
	int top;
	int bot;
	int left;
	int right;
} shape;

/* Information about each line */
static struct
{
	char data[MAXLINE];
	int plx, prx; /* Prev line left and right x */
	int blx, bly, trx, try; /* BBox of entire line */
	float AvgSize;
	int Size;
	int Words;
} CL;

static enum
{
  EMPTY,
  STRING,
  HLINE,
  VLINE
} LastItem = EMPTY;

static void Init_Shape()
{
	shape.Found = FALSE;
	shape.top = 0;
	shape.bot = 9999;
	shape.left = 9999;
	shape.right = 0;
}

/* Initializing data structures */
static void Init(FILE *file)
{
int i;

	FILE *fp;
	t.rd = file;
	t.scale = 1.0;
	t.metricsSize = t.encodingSize = t.fontSize = 100;

	t.metrics = (MetricsTable *)malloc(t.metricsSize * sizeof(Metrics));
	for(i=0;i<t.metricsSize;i++)(*t.metrics)[i] = NULL;

	t.encoding = (EncodingTable *)malloc(t.encodingSize * sizeof(Encoding));
	for(i=0;i<t.encodingSize;i++)(*t.encoding)[i] = NULL;

	t.font = (FontTable *)malloc(t.fontSize * sizeof(Font));
	for(i=0;i<t.fontSize;i++)(*t.font)[i] = NULL;

	t.lbuf = 0;
	if (FS)
		t.Prev_FontSize = 9999;
	t.nonEmptyPage = FALSE;
	t.blx = t.bly = t.trx = t.try = 0;

	table.Opened = FALSE;

	Init_Shape();

	/* This module is for calculating text Bounding Box */
	/* Try to speculate min and max text coordinates */
	CL.plx = 9999;
	CL.prx = 0;
	fp = fopen("/tmp/minmax.tmp", "r");
	if (fp != (FILE *)NULL)
	{
		int tmp;
		int i, min, max;
		int Left[1000];
		int Right[1000];
		char *tmp1, *tmp2;
		tmp1 = (char *)malloc(3*sizeof(char));
		if (tmp1 == (char *)NULL)
		{ perror("malloc"); exit(1); }
		tmp2 = (char *)malloc(6*sizeof(char));
		if (tmp2 == (char *)NULL)
		{ perror("malloc"); exit(1); }
		for (i=0; i<1000; i++)
		{
			Left[i] = 0;
			Right[i] = 0;
		}
		while (fscanf(fp, "%s%s", tmp1, tmp2) != EOF)
		{
			tmp = (int)atof(tmp2)/100;
			if (!strcmp(tmp1, "ML"))
				Left[tmp]++;
			if (!strcmp(tmp1, "MR"))
				Right[tmp]++;
		}
		for (i=999, min=0; i>=0; i--)
		{
			if (Left[i] >= min)
			{
				CL.plx = i;
				min = Left[i];
			}
		}
		for (i=0, max=0; i<1000; i++)
		{
			if (Right[i] >= max)
			{
				CL.prx = i;
				max = Right[i];
			}
		}
	}
	fclose(fp);

	CL.blx = 9999;
	CL.bly = 9999;
	CL.trx = 0;
	CL.try = 0;

	if ((BBox_Top == 832) && (BBox_Right == 577))
	{ BBox_Top = 842; BBox_Right = 596; }
	if ((BBox_Top == 842) && (BBox_Right == 596))
		strcpy(PaperSize, "-sPAPERSIZE=a4");
	else if ((BBox_Top == 792) && (BBox_Right == 612))
		strcpy(PaperSize, "-sPAPERSIZE=letter");
	else strcpy(PaperSize, "");

}

/* Reads an integer from input stream */
static int ReadInt()
{
	int i;
	if (fscanf(t.rd, "%d", &i) == EOF) endoffile();
	return abs(i);
}

/* Useful for calculating how many pixels
 * should be cropped from a page,
 * in order for an image to be accurately extracted.
 */
double ComputeCrop(int flag, double n)
{
	if (flag == TOP)
		return (BBox_Top-n-5);
	else return (double)n;
}

static void ParseEncoding() 
{
	/* Parse QE directive. */
	int e = ReadInt(), n = ReadInt(), i, tooSparse;
	Encoding enc;
	if (e<0) crash("negative encoding number");
	if (n>256) crash("too many glyph indexes");

	/* Grow "t.encoding" if necessary. */
	if (t.encodingSize<=e)
	{
		int oldSize = t.encodingSize;
		t.encodingSize = 2*e;
		t.encoding = (EncodingTable *)realloc(
			     (char *)t.encoding,
			     t.encodingSize * sizeof(Encoding));
	for(i=oldSize;i<t.encodingSize;i++)(*t.encoding)[i] = NULL;
	}

	/* If this is the first encoding numbered "e", allocate array. */
	if ((*t.encoding)[e] == NULL)
	   (*t.encoding)[e] = (EncodingVector *)malloc(sizeof(EncodingVector));

	enc = (*t.encoding)[e];

	for (i = 0 ; i<256 ; i++)
		(*enc)[i] = (i<n) ? ReadInt() : NonstandardGlyph;

	/* Some applications build the encoding vector incrementally.  If
	this one doesn't have at least the lower-case letters, we augment
	it with ASCII. */
	for (i = 'a'; i<='z'; i++)
		tooSparse = (*enc)[i] == NonstandardGlyph;
	if (tooSparse)
		for (i = 0; i<256; i++)
			if ((*enc)[i] == NonstandardGlyph) (*enc)[i] = i;

}

#define GuessAscend 0.9
#define GuessDescend -0.2

static void ParsePair(double *x, double *y)
{
	*x = ReadInt() / 100.0;
	*y = ReadInt() / 100.0;
}

static void ParseFont()
{
	/* Parse QF directive. */
	int n = ReadInt(), i;
	Metrics mt;
	Font f;
	double xmax, bly, try;
	if (n<0) crash("negative font number");

	/* Grow "t.font" if necessary. */
	if (t.fontSize<=n)
	{
		int oldSize = t.fontSize;
		t.fontSize = 2*n;
		t.font = (FontTable *)realloc(
			 (char *)t.font,
			 t.fontSize * sizeof(Font));
	for(i=oldSize;i<t.fontSize;i++)(*t.font)[i] = NULL;
	}

	/* If this is the first font numbered "n", allocate "FontRec". */
	if ((*t.font)[n] == NULL)
		(*t.font)[n] = (Font)malloc(sizeof(FontRec));

	f = (*t.font)[n];
	ParsePair(&f->x, &f->y);

	/* QF directive is acting in a rather peculiar way 
	 * Fonts created by dvips have a different range than fonts
	 * by MSWord. So we try to adjust them.
	 */

	/* Calculate and adjust the html font size */
	if (FS)
	{
		if (f->x > 100) 
			f->Html_FontSize = floor((((f->x/20)-5)*FS)/10);
		else f->Html_FontSize = floor(((f->x-5)*FS)/10);
	}

	ParsePair(&f->xp, &f->yp);
	f->e = ReadInt();
	if ((*t.encoding)[f->e] == NULL) crash("undefined encoding number");
	f->m = ReadInt();
	mt = (*t.metrics)[f->m];
	if (mt == NULL) crash("undefined metric number");

	/* Read and parse the font's name */
	fscanf(t.rd, "%s", f->name);

	/* Check for font's weight (Bold, Italic, ...) */
	f->w = ParseFontName(f->name);

	/* If font == ISOLatinGreek there is a gap in it's encoding */
	/* Try to fill it now */
	if (strstr(f->name, "ISOLatinGreek"))
	{
		int i;

		for (i=0; i<256; i++)
			if ((*t.encoding)[f->e][0][i] == 9999)
				(*t.encoding)[f->e][0][i] = i;
	}

	/* Transform height of font bounding box to reporting coordinates: */
	f->bx = f->xp * mt->bly / 1000.0;
	f->by = f->yp * mt->bly / 1000.0;
	f->tx = f->xp * mt->try / 1000.0;
	f->ty = f->yp * mt->try / 1000.0;

	/* In some fonts produced by dvips, the FontBBox is incorrectly
	   defined as [0 0 1 1].  We check for this, and apply the same
	   heuristic used for an undefined FontBBox in "ParseMetrics".  */
	if (f->by-f->ty < 1.1)
	{
		xmax = 0.0;
		for (i = 0; i<256; i++)
		    if (mt->chr[i].x > xmax)
			xmax = mt->chr[i].x;
		bly = GuessDescend * xmax;
		try = GuessAscend * xmax;
		f->bx = f->xp * bly / 1000.0;
		f->by = f->yp * bly / 1000.0;
		f->tx = f->xp * try / 1000.0;
		f->ty = f->yp * try / 1000.0;
	}
}

/* Identifies a font's weight */
int ParseFontName(char *name)
{
	char *w;

	if (!strcmp(name, "UserDef"))
		return UNKNOWN;
	if ((w = (char *)strchr(name, '-')) != (char *)NULL)
	{
		w++;
		if (strstr(w, "Bol") || strstr(w, "bol"))
			return BOLD;
		if (strstr(w, "Italic"))
			return ITALIC;
	}
	else return NORMAL;
}
	
static void ParseMetrics()
{
	/* Parse QM directive. */
	int m = ReadInt(), i;
	Metrics mt;

	if (m<0) crash("negative metric number");

	/* Grow "t.metrics" if necessary. */
	if (t.metricsSize<=m)
	{
		int oldSize = t.metricsSize;
		t.metricsSize = 2*m;
		t.metrics = (MetricsTable *)realloc(
			    (char *)t.metrics,
			    t.metricsSize * sizeof(Metrics));
		for(i=oldSize;i<t.metricsSize;i++)(*t.metrics)[i] = NULL;
	}

	/* If this is the first metrics numbered "m", allocate "MetricsRec". */
	if ((*t.metrics)[m] == NULL)
		(*t.metrics)[m] = (Metrics)malloc(sizeof(MetricsRec));

	mt = (*t.metrics)[m];

	ParsePair(&mt->blx, &mt->bly);
	ParsePair(&mt->trx, &mt->try);

	for (i = 0; i<256; i++) ParsePair(&mt->chr[i].x, &mt->chr[i].y);

	if (mt->blx == 0.0 && mt->bly == 0.0 && mt->trx == 0.0 && mt->try == 0.0)
	{
		/* "FontBBox" was not specified; take a guess. */
		for (i = 0; i<256; i++)
			if (mt->chr[i].x > mt->trx) mt->trx = mt->chr[i].x;
		mt->bly = GuessDescend * mt->trx;
		mt->try = GuessAscend * mt->trx;
	}
}

/* Returns TRUE if a word is only but dots */
int LotsOfDots(char *buf)
{
	int i;
	char *p,*s;

	i=0;
	s = buf;
	while (p = strchr(s, '.'))
	{
		i++;
		p++;
		s = p;
	}
	if (i>3) return TRUE;
	else return FALSE;
}

/* Returns TRUE if a number followed by a dot
 * is encountered.
 */
int NumberDot(char *s)
{
	char *p;

	p = s;
	if (isdigit(*p))
	{
		p++;
		while (isdigit(*p)) p++;
		if ((*p == '.') && (*(p+1) == '\0'))
			return TRUE;
		else return FALSE;
	}
	else if (*p == '[')
		{
			p++;
			while (isdigit(*p)) p++;
			if ((*p == ']') && (*(p+1) == '\0'))
				return TRUE;
			else return FALSE;
		}
		else return FALSE;
}

/* Transforms Latin numbers to Arabic */
int LatinToNum(char *str)
{
	int i=0;
	int sum=0;

	if (isdigit(str[0]))
		return atoi(str);
	while (str[i] != '\0')
	{
		switch (str[i])
		{
			case 'i': sum += 1;
			break;
			case 'v': sum += 5;
				  if (i != 0)
					if (str[i-1] == 'i')
						sum -= 2;
			break;
			case 'x': sum += 10;
				  if (i != 0)
					if (str[i-1] == 'i')
						sum -= 2;
			break;
			default: return 9999;
		}
		i++;
	}
	return sum;
}

/* This is where main job is done.
 * Output() performs a great variety of actions,
 * in order to describe a word's position and identity
 * concering it's surrounding environment.
 */
int Output()
{
	double x0, y0, x1, y1, x2, y2, x3, y3;
	int blx, bly, trx, try, mid;
	BOOLEAN Wopen=TRUE;
	int FontSize;
	Font f;
	int sz;

	sz = t.lbuf;
	f = (*t.font)[t.f];
	if (f == (FontRec *)NULL)
		return 1;

	/* Compute the corners of the parallelogram with width "(t.x0,t.y0)"
   	to "(t.x1,t.y1)" and height "(f.bx,f.by)" to "(f.tx,f.ty)". Then
   	compute the bottom left corner and the top right corner of the
   	bounding box (rectangle with sides parallel to the coordinate
   	system) of this rectangle. */
	x0 = t.x0 + f->bx; y0 = t.y0 + f->by;
	x1 = t.x1 + f->bx; y1 = t.y1 + f->by;
	x2 = t.x0 + f->tx; y2 = t.y0 + f->ty;
	x3 = t.x1 + f->tx; y3 = t.y1 + f->ty;

	blx = abs(floor(t.scale * MIN(MIN(MIN(x0, x1), x2), x3)));
	bly = abs(floor(t.scale * MIN(MIN(MIN(y0, y1), y2), y3)));
	trx = abs(floor(t.scale * MAX(MAX(MAX(x0, x1), x2), x3)));
	try = abs(floor(t.scale * MAX(MAX(MAX(y0, y1), y2), y3)));

	/* A figure ends when one of the following words is encountered */
	if (shape.Found)
	{
		if ((strstr(t.buf, "Figure")) ||
		    (strstr(t.buf, "Fig"))    ||
		    (strstr(t.buf, "Σχημα"))  ||
		    (strstr(t.buf, "Σχήμα")))
		{
			ParseImage(shape.top, shape.bot, shape.left, shape.right);
			Init_Shape();
		}
		else return 1;
	}

	/* Output word separator if this isn't first word on page. */
	if (t.nonEmptyPage)
	{
		int mid = try - bly;
		if ( ( (try <= t.try) && (bly >= t.bly) ) ||
		     ( (try >= t.try) && (bly <= t.bly) ) ||
		     ( (try > t.try) && (bly < t.try) ) ||
		     ( (try > t.bly) && (bly < t.bly) ) )
			AppendWord(" "); /* Same line */
		else
		{
		    /* Have we found any kind of line ??? */
		    if (LineHead != (struct LineNode *)NULL)
		    {
			if (LineHead->VorH == HORIZ)
			{
			    /*
			     * Last line we found was HORIZONTAL.
			     * This means either that a table
			     * should be closed or that a simple
			     * horizontal line needs to be proccessed.
			     */
			    if (table.Opened == TRUE)
			    {
				AppendWord("</TABLE>\n\n");
				if (Align == CENTRAL)
					AppendWord("</CENTER>\n");
				table.Opened = FALSE;
			    }
			    else AppendBreak(try);
			}
			else {;}
		    }
		    else AppendBreak(try);
		    ShowLine();
		}
	}

	/* Check whether we have a change in font's weight */
	if (FontWeight != f->w)
		switch (FontWeight)
		{
			case NORMAL: break;
			case BOLD: AppendWord("</B>");
				Wopen = FALSE;
				break;
			case ITALIC: AppendWord("</I>");
				Wopen = FALSE;
				break;
			default: break;
		}

	/* Compute the font size of the string which is to be output */
	if (FS)
	{
		FontSize = f->Html_FontSize;
		if (FontSize != t.Prev_FontSize)
		{
			char *temp;
			char *tmpW1 = strdup("");
			char *tmpW2 = strdup("");
			temp = (char *)malloc(30*sizeof(char));
			if (temp == (char *)NULL)
			{ perror("malloc"); exit(1); }
			if (Wopen)
			{
				if (FontWeight == BOLD)
				{
					tmpW1 = strdup("</B>");
					tmpW2 = strdup("<B>");
				}
				if (FontWeight == ITALIC)
				{
					tmpW1 = strdup("</I>");	
					tmpW2 = strdup("<I>");
				}
			}
			sprintf(temp, "%s</FONT>\n<FONT SIZE=%d>%s\n", tmpW1, FontSize, tmpW2);
			AppendWord(temp);
			free(temp);
		}
	}


	/* Check whether we have a change in font's name */
	if (strcmp(f->name, t.Prev_FontName))
	{
		char *temp=(char *)malloc(55*sizeof(char));
		if (temp == (char *)NULL)
		{ perror("malloc"); exit(1); }
		sprintf(temp, "<!-- %s -->\n", f->name);
		AppendWord(temp);
		free(temp);
	}

	/* Check whether we have a change in font's weight */
	if (FontWeight != f->w)
	{
		switch (f->w)
		{
			case NORMAL: break;
			case BOLD: AppendWord("<B>"); break;
			case ITALIC: AppendWord("<I>"); break;
			default: break;
		}
		FontWeight = f->w;
	}

	/* Check the case of footnotes */
	/* Try to speculate their existance */
	if (
		( (t.buf[0] == '*') ||		/* asterisk */
		  (t.buf[0] == '+') ||		/* dagger */
		  (isdigit(toascii(t.buf[0])))  /* digit */
		) && 
		(FontSize < t.Prev_FontSize)    /* font size reduction */
	   )
	{
		char *link, *name;
		link = (char *)malloc(100*sizeof(char));
		if (link == (char *)NULL)
		{ perror("malloc"); exit(1); }
		name = (char *)malloc(30*sizeof(char));
		if (name == (char *)NULL)
		{ perror("malloc"); exit(1); }
		name[0] = '\0';
		link[0] = '\0';
		if (LastItem == HLINE)  /* This is the first Foot Note */
		{			/* There may be others */
			sprintf(name,"<A NAME=\"F0_%d_%d\"></A>\n", Page, FootParsed);
			sprintf(link, "<A HREF=\"%s#F1_%d_%d\">*</A>\n", HTMLName, Page, FootParsed);
			FootParsed++;
			FootNotes = TRUE;
			/* FootNotes remains TRUE */
			/* until a newpage is reached */
		}
		else if (FootNotes) /* This must be the Foot Note Text */
		     {
			sprintf(name,"<A NAME=\"F0_%d_%d\"></A>\n", Page, FootParsed);
			sprintf(link, "<A HREF=\"%s#F1_%d_%d\">*</A>\n", HTMLName, Page, FootParsed);
			FootParsed++;
			FootNotes = TRUE;
		     }
		     else if (!EmptyLine) /* This is a Foot Note Mark */
		     {	
			char *tmp = (char *)malloc(15*sizeof(char));
			if (tmp == (char *)NULL)
			{ perror("malloc"); exit(1); }
			sprintf(tmp, "F1_%d_%d", Page, FootStar);
			sprintf(name, "<A NAME=\"F1_%d_%d\"></A>\n", Page, FootStar);
			sprintf(link, "<A HREF=\"%s#F0_%d_%d\">*</A>\n", HTMLName, Page, FootStar);
			ReportWarning("FootNote", tmp, 10);
			FootStar++;
		     }
		if (name[0] != '\0')
		{
			t.buf[0] = '\0';
			t.lbuf = strlen(HTMLName);
			strcat(t.buf, name);
			t.lbuf += 23;
			strcat(t.buf, link);
			t.lbuf += 25;
		}
		free(name);
		free(link);
	}

	t.buf[t.lbuf] = '\0';

	/* Check for lists */
	if (EmptyLine)
	    if (t.buf[0] == '.')
	    {
		if (UListsOpen)
		{
			if (OListsOpen && (OList_start > UList_start))
			{
				AppendWord("</OL>");
				OListsOpen--;
				OList_start=0;
			}
			AppendWord("<LI>");
		}
		else
		{
		    char *link = (char *)malloc(15*sizeof(char));
		    if (link == (char *)NULL)
		    { perror("malloc"); exit(1); }
		    sprintf(link, "Warn%d", ErrorNum++);
		    fprintf(out, "<A NAME=\"%s\"></A>\n", link);
		    ReportWarning("Unordered List", link, 5);

		    AppendWord("<UL>\n<LI>");
		    UListsOpen++;
		}
		UList_start = trx;
		t.buf[0] = '\0';
	    }
	    else
	    {
		if (UListsOpen && (blx < UList_start))
		 {
		    AppendWord("</UL>\n");
		    UListsOpen--;
		    UList_start=0;	
		 }
	    }

	/* Check for ordered list */
	if (EmptyLine)
	    if (NumberDot(t.buf))
	    {
		if (OListsOpen)
		{
			if (UListsOpen && (UList_start > OList_start))
			{
				AppendWord("</UL>");
				UListsOpen--;
				UList_start=0;
			}
			AppendWord("<LI>");
		}
		else
		{
		    char *link = (char *)malloc(15*sizeof(char));
		    if (link == (char *)NULL)
		    { perror("malloc"); exit(1); }
		    sprintf(link, "Warn%d", ErrorNum++);
		    fprintf(out, "<A NAME=\"%s\"></A>\n", link);
		    ReportWarning("Ordered List", link, 5);

		    AppendWord("<OL>\n<LI>");
		    OListsOpen++;
		}
		OList_start = trx;
		t.buf[0] = '\0';
	    }
	    else
	    {
		if (OListsOpen && (blx < OList_start))
		 {
		    AppendWord("</OL>\n");
		    OListsOpen--;
		    OList_start=0;	
		 }
	    }

	/* When TOC is TRUE we try to make a link
	 * out of the number at end of line.
	 */
	if (TableOfCon)
	{
		if (t.buf[0] != '.')
		{
		    if (LatinToNum(t.buf) != 9999)
		    {
		        char *tmp = (char *)malloc(200*sizeof(char));
			if (tmp == (char *)NULL)
			{ perror("malloc"); exit(1); }
		     	sprintf(tmp, "<A HREF=\"%s#PAGE%d\">%d</A>", HTMLName, LatinToNum(t.buf)+PagesPlus, LatinToNum(t.buf)+PagesPlus);
		     	AppendWord(tmp);
		     	TableOfCon = FALSE;
		     	Dots = 0;
			free(tmp);
		    }
		    else
		    {
			AppendWord(t.buf);
			TableOfCon = FALSE;
			Dots = 0;
		    }
		}
		else AppendWord(t.buf);
	}
	else AppendWord(t.buf);

	/* We know where a TOC lies by counting
	 * number of dots in a line.
	 */
	if (LotsOfDots(t.buf) || (Dots > 3))
		TableOfCon = TRUE;

	if (t.buf[0] == '.')
		Dots++;
	else Dots = 0;

	if (t.buf[0] != '<')
		EmptyLine = FALSE;

	/* Output is "forced" when an horizontal line
	 * or End of Page have been encountered.
	 */
	if (ForceOutput)
	{
		ShowLine();
		ForceOutput = FALSE;
	}

 
	/* Updating instances "t" and "CL" */
	t.lbuf = 0;
	t.buf[0] = '\0';
	LastItem = STRING;
	t.nonEmptyPage = TRUE;
	if (blx < CL.blx) CL.blx = blx; 
	if (bly < CL.bly) CL.bly = bly; 
	if (trx > CL.trx) CL.trx = trx; 
	if (try > CL.try) CL.try = try;
	CL.Size += sz;
	CL.Words += 1;
	if (FS)
		t.Prev_FontSize = FontSize;
	strcpy(t.Prev_FontName, f->name);
	t.blx = blx; t.bly = bly; t.trx = trx; t.try = try;

	/* Was there an image before this string ??? */
	if (UnDone)
	{
		ImageHead->CropBot = ComputeCrop(BOT,t.try);
		UnDone = FALSE;
	}
}

int ReportWarning(char *what, char *link, int percent)
{
	char *alink = (char *)malloc(100*sizeof(char));
	if (alink == (char *)NULL)
	{ perror("malloc"); exit(1); }

	sprintf(alink, "<A HREF=\"%s#%s\">Show me</A>\n", HTMLName, link); 
	fprintf(ErrorTbl, "  <TR>\n");
	fprintf(ErrorTbl, "    <TD ALIGN=MIDDLE>%s</TD>\n", what);
	fprintf(ErrorTbl, "    <TD ALIGN=MIDDLE>%s</TD>\n", alink);
	fprintf(ErrorTbl, "    <TD ALIGN=MIDDLE>%d%%</TD>\n", percent);
	fprintf(ErrorTbl, "  </TR>\n");
}

static int SameDirection(double x0, double y0, double x1, double y1)
{
	return  y0 == 0.0 && y1 == 0.0 && x0*x1 > 0.0
		|| x0 == 0.0 && x1 == 0.0 && y0*y1 > 0.0
		|| x0 * y1 == x1 * y0;
}

static void ParseString()
{
/* Parse QS directive. */
#define spaceTol 0.3 /* fraction of average character width to signal word break */
	char buf[1000];
	int n, ch, i, j, in, l;
	Font f;
	Encoding enc;
	GlyphIndex glyph;
	double x0, y0, x1, y1, xsp, ysp, dx, dy, maxx, maxy;

#define SetBuf() \
  do { \
  strncpy(t.buf, buf, l); \
  t.lbuf = l; \
  t.f = n; \
  t.x0 = x0; t.y0 = y0; t.x1 = x1; t.y1 = y1; \
  } while(0)

	n = ReadInt(); /* index in "t.font" */
	f = (*t.font)[n];
	if (f == NULL) crash("undefined font number");
	enc = (*t.encoding)[f->e];
	if (enc==NULL) crash("undefined encoding number");
	ParsePair(&x0, &y0); /* initial currentpoint */
	j = ReadInt(); /* length of string */
	ch = getc(t.rd);
	if (ch == EOF) endoffile();
	if (ch != ' ') crash("expected space in string directive");

	l = 0;
	for (i = 0; i<=j-1; i++)
	{
	    char *str;
	    int lstr;

	    glyph = fgetc(t.rd);
	    if (glyph==EOF) endoffile();

	    if (glyph <= 255)
	    {
	    	if (!strcmp(f->name, "UserDef"))
	    	{
		    if (EnCoding == NORMAL)
		    	 str = DvipsGlyphs[glyph];
		    else if (EnCoding == MATH)
			 str = DvipsMathGlyphs[glyph];
		    else str = ManualDvipsGlyphs[glyph];
	    	}
		else if (strstr(f->name, "Symbol"))
		    str = SymbolGlyphs[glyph];
		else if ((!strcmp(f->name, "/Times")) ||
			 (!strcmp(f->name, "/Helvetica")) ||
			 (!strcmp(f->name, "/Courier")))
			str = ISOLatin1Glyphs[glyph];
	        else str = AsciiGlyphs[glyph];
	    }

	    lstr = strlen(str);
	    strncpy(&buf[l], str, lstr);
	    l += lstr;

	    if (buf[l-1] == '\255') buf[l-1] = ' ';
    }

	ParsePair(&x1, &y1); /* final currentpoint */
	if (l != 0) /* "l==0" e.g., when Adobe Illustrator outputs "\r" */
	{ 
	if (t.lbuf == 0) 
		{SetBuf();}
	else {
        /* If the distance between this string and the previous one is
           less than "spaceTol" times the minimum of the average
           character widths in the two strings, and the two strings
           are in the same direction, then append this string to the
           previous one.  Otherwise, output the previous string and
           then save the current one.

           Sometimes this string overlaps the previous string, e.g.,
           when TeX is overprinting an accent over another character.
           So we make a special case for this (but only handle the
           left-to-right orientation). */

        /* Set "(xsp,ysp)" to the reporting space coordinates of the
           minimum of the average width of the characters in this
           string and the previous one. */

        xsp = MIN((t.x1-t.x0) / t.lbuf, (x1-x0) / l);
        ysp = MIN((t.y1-t.y0) / t.lbuf, (y1-y0) / l);

        dx = x0-t.x1;
        dy = y0-t.y1;
        maxx = spaceTol * xsp;
        maxy = spaceTol * ysp;
	if (dx*dx + dy*dy < maxx*maxx + maxy*maxy
	    || t.y1 == y0 && t.x0 <= t.x1 && t.x0 <= x0 && x0 <= t.x1
	    && SameDirection(t.x1-t.x0, t.y1-t.y0, x1-x0, y1-y0))
	    {
	  	if (t.lbuf+l >= sizeof(t.buf))
		{
            		Output();
            		SetBuf();
	  	}
          	else
		{ 
            		strncpy(&t.buf[t.lbuf], buf, l);
            		t.lbuf += l;
            		t.x1 = x1; t.y1 = y1;
	    		/* *** Merge font bounding boxes? */
	  	}
	    }
	    else
	    {
		Output();
		SetBuf();
	    }
	}
	}
}

int ParseImage(int T, int B, int L, int R)
{
	struct ImageNode *ip;
	char *link = (char *)malloc(15*sizeof(char));
	if (link == (char *)NULL)
	{ perror("malloc"); exit(1); }

    if (!UnDone)
    {
	ip = (struct ImageNode *)malloc(sizeof(struct ImageNode));
	ip->next = (struct ImageNode *)NULL;
	ip->prev = (struct ImageNode *)NULL;

	/* Which page we are on */
	ip->Page = Page;

	/* Which image is this */
	ip->ImageNum = ImageNum;
	ImageNum++;

	/* Check out whether this image is the first thing in page */
	if (t.nonEmptyPage)
	{
		if (T != 0)
			ip->CropTop = ComputeCrop(TOP, T);
		else ip->CropTop = ComputeCrop(TOP, t.bly);
	}
	else ip->CropTop = 0.0;

	/* Do we know where image ends ? */
	if (B != 0)
		ip->CropBot = B-5;
	else /* Cant' tell. We 'll fill that later */
	{
		ip->CropBot = 0.0;
		UnDone = TRUE;
	}

	/* For the time being we forget about left and right cropping */
	ip->CropLeft = 0.0;
	ip->CropRight = 0.0;

	/* Link nodes together */
	if (ImageHead == (struct ImageNode *)NULL)
		ImageTail = ip;
	else ImageHead->prev = ip;
	ip->next = ImageHead;
	ImageHead = ip;

	if (ImageHead != ImageTail)
	    if (ImageHead->Page == ImageHead->next->Page)
		ImageInPage++;
	    else ImageInPage=1;

	/* Make link for this image */
	sprintf(link, "Warn%d", ErrorNum++);
	fprintf(out, "<A NAME=\"%s\"></A>\n", link);
	ReportWarning("Image", link, 10);
	fprintf(out, "\n<BR>\n");
	if (Align == CENTRAL)
		fprintf(out, "<CENTER>\n");
	fprintf(out,"<IMG SRC=\"I%03d_%d.gif\">\n",Page, ImageInPage);
	if (Align == CENTRAL)
		fprintf(out, "</CENTER>\n");
	fprintf(out, "<BR>\n");
    }
}

int ParseLine()
{
	char *link;
	double x, y, width, height;
	struct LineNode *rp;

	/* Reading line information */
	ParsePair(&x, &y);
	ParsePair(&width, &height);

	/* Appending information into the Line list */
	rp = (struct LineNode *)malloc(sizeof(struct LineNode));
	rp->LineX = (int)x;
	rp->LineY = (int)y;
	rp->LineW = (int)width;
	rp->LineH = (int)height;
	if (!rp->LineW)
		rp->VorH = VERTIC;
	else rp->VorH = HORIZ;
	rp->next = LineHead;
	LineHead = rp;

	/*
	 * Start parsing the lines we have found so far.
	 * H and V means that either a new table or a new
	 * table row starts. Act accordingly ...
	 */
	if (rp->VorH == VERTIC)
	{
	    if (LineHead->next->VorH == HORIZ)
	    {
		if (table.Opened == FALSE)
		{
			/* There was no open table before this ... */
			/* It will enter this module only once per table */
			table.LeftX = LineHead->LineX;
			table.UpperY = LineHead->next->LineY;
			table.RightX = LineHead->next->LineX +
					LineHead->next->LineW;

			/* Check for any unclosed lists */
			if (UListsOpen)
				if (table.LeftX < UList_start)
				{
					strcat(t.buf, "\n</UL>\n");
					t.lbuf += 7;
					UListsOpen--;
					UList_start = 0;
				}
			if (OListsOpen)
				if (table.LeftX < OList_start)
				{
					strcat(t.buf, "\n</OL>\n");
					t.lbuf += 7;
					OListsOpen--;
					OList_start = 0;
				}

			/* Check for any unfinished images */
			if (UnDone)
			{
				ImageHead->CropBot = ComputeCrop(BOT,t.try);
				UnDone = FALSE;
			}
			link = (char *)malloc(15*sizeof(char));
			if (link == (char *)NULL)
			{ perror("malloc"); exit(1); }
			sprintf(link, "Warn%d", ErrorNum++);
			fprintf(out, "<A NAME=\"%s\"></A>\n", link);
			ReportWarning("Table", link, 20);
			if (Align == CENTRAL)
				strcat(t.buf, "<CENTER>\n");
			strcat(t.buf, "<TABLE BORDER=1>\n");
			strcat(t.buf, "<TR>\n  <TD>");
			t.lbuf += 37;
		}
		else /* New table row */
		{	
			strcat(t.buf, "<TR><TD>");
			t.lbuf += 8;
		}
		table.Opened = TRUE;
	    }
	    else
	    {
		strcat(t.buf, "</TD>\n");
		t.lbuf += 6;
		t.buf[t.lbuf] = '\0';
		if (LineHead->LineX < table.RightX)
		{
			strcat(t.buf, "<TD>");
			t.lbuf += 4;
		}
		else
		{
			strcat(t.buf, "</TR>\n");
			t.lbuf += 6;
		}
	    }
	    LastItem = VLINE;
	}
	else 
	{
		if (t.nonEmptyPage)
		{
			ForceOutput = TRUE;
			Output();
		}
		LastItem = HLINE;
	}
}

int ParseShape()
{
	double x, y, w, h;

	ParsePair(&x, &y);
	ParsePair(&w, &h);

	if (LastItem == STRING)
		Init_Shape();
	if (x < shape.left)
		shape.left = (int)x;
	if (x > shape.right)
		shape.right = (int)x;
	if (y > shape.top)
		shape.top = (int)y;
	if (y < shape.bot)
		shape.bot = (int)y;
	if (shape.top != shape.bot)
		shape.Found = TRUE;
	LastItem = HLINE;
}

int print_Llist()
{
	struct LineNode *rp;

	fprintf(stderr, "\nX\tY\tW\tH\tWhat?\n");
	for (rp = LineHead;
	     rp != (struct LineNode *)NULL;
	     rp = rp->next)
		fprintf(stderr, "%d\t%d\t%d\t%d\t%s\n", rp->LineX, rp->LineY, rp->LineW, rp->LineH, (rp->VorH)?"Horizontal":"Vertical");
}

int print_Ilist()
{
	struct ImageNode *ip;

	fprintf(stderr, "\nPageN\tImageN\tTop\tBot\tLeft\tRight\n");
	for (ip = ImageTail;
	     ip != (struct ImageNode *)NULL;
	     ip = ip->prev)
		fprintf(stderr, "   %d\t   %d\t%d\t%d\t%d\t%d\n", ip->Page, ip->ImageNum, ip->CropTop, ip->CropBot, ip->CropLeft, ip->CropRight);
}

int AppendWord(char *s)
{
	strcat(CL.data, s);
}

int AppendBreak(int try)
{
	strcat(CL.data, "<BR>");
	if (try < t.bly)
		switch ((int)((t.bly - try)/MinDist))
		{
			case 0:  break;
			case 1:  strcat(CL.data, "<BR>"); break;
			case 2:  strcat(CL.data, "<BR><BR>"); break;
			case 3:  strcat(CL.data, "<BR><BR><BR>"); break;
			default: strcat(CL.data, "<BR><BR><BR><BR>");
		}
}

int ShowLine()
{
	int i;
	BOOLEAN Center = FALSE;

	/* if (Math)
	{
		CL.AvgSize = (float)CL.Size/(float)CL.Words;
		if ((CL.Words > 2) && (CL.AvgSize < 2))
		{
			fprintf(out, "---(Mathematical Expression)---\n");
			if (Math2Image)
				ParseLine(CL.try, CL.bly, 0, 0);
			FlushLine();
			return 0;
		}
	} */
	if (Align == CENTRAL)
	if (!UListsOpen && !OListsOpen)
	    if (CL.blx > CL.plx)
		if ( abs((CL.blx - CL.plx) -
		      (CL.prx - CL.trx)) < Diff)
		{
			fprintf(out, "<CENTER>\n");
			Center = TRUE;
		}
		else fprintf(out, "&nbsp;&nbsp;");

	fprintf(out, "%s\n", CL.data);

	if (Align == CENTRAL)
		if (Center) fprintf(out, "</CENTER>\n");
	FlushLine();
}

int FlushLine()
{
	EmptyLine = TRUE;
	CL.data[0] = '\0';
	CL.blx = 9999;
	CL.bly = 9999;
	CL.trx = 0;
	CL.try = 0;
	CL.AvgSize = 0.0;
	CL.Size = 0;
	CL.Words = 0;
}

BOOLEAN PageIndex(char *s)
{
	char *p;

	/* Check for arabic numbers */
	p = s;
	if (*p == '\0') return TRUE;
	while (isdigit(*p))
		p++;
	if (*p == '\0')
		return TRUE;

	/* Check for -1- kind of indexing */
	p = s;
	if (*p == '-') p++;
	while (isdigit(*p))
		p++;
	if ((*p == '-') && (*(p+1) == '\0'))
		return TRUE;

	/* Check for latin numbers */
	p = s;
	if (  ((*p == 'i')
	    || (*p == 'v')
	    || (*p == 'x'))
	    && PageIndex(p+1))
		return TRUE;
	
	return FALSE;
}

int Filter(FILE *file)
{
	int c;

	Init(file);
	for ( ; ; )
	{
		do
		{
			c = fgetc(t.rd);
			if (c==EOF)
				return (Page-1);
		} while (c!='Q');
		c = fgetc(t.rd);
		switch (c)
		{
			case 'M': ParseMetrics();
			break;
			case 'E': ParseEncoding();
			break;
			case 'F': ParseFont();
			break;
			case 'S': ParseString();
			break;
			case 'L':
				if (t.lbuf > 0)
				{
					t.buf[t.lbuf] = '\0';
					/* ForceOutput = TRUE; */
					/* Output(); */
				}
				ParseLine();
			break;
			case 'R':
				if (t.lbuf > 0)
				{
					t.buf[t.lbuf] = '\0';
					ForceOutput = TRUE;
					Output();
				}
				ParseShape();
			break;
			case 'I': 
				if (t.lbuf > 0)
				{
					t.buf[t.lbuf] = '\0';
					ForceOutput = TRUE;
					Output();
				}
				ParseImage(0, 0, 0, 0);
			break;
			case 'C':/* copypage */
			case 'P':/* showpage */
                		if (t.lbuf > 0)
				{
		  			t.buf[t.lbuf] = '\0';
					if (PageIndex(t.buf))
						t.buf[0] = '\0';
					ForceOutput = TRUE;
		  			Output();
				}
				if (shape.Found)
				{
					ParseImage(shape.top, shape.bot, shape.left, shape.right);
					Init_Shape();
				}
				fprintf(out, "<BR>\n");
				fprintf(out, "<A NAME=\"PAGE%d\"></A>\n", Page+1);
				if (UnDone) UnDone = FALSE;
				Page++;
                		t.nonEmptyPage = FALSE;
				FootNotes = FALSE;
				FootParsed = 0;
				FootStar = 0;
                		t.blx = t.bly = t.trx = t.try = 0;
				if (MyDebug)
				{
					print_Ilist();
					print_Llist();
				}
                	break;
			case 'Z': /* erasepage */ 
				  /* skip */ 
			break;
			case EOF: return (Page-1);
			/* default: skip */
		}
	} 
/* Finalize. */
}
