/*
 * pstoobj.c - Convert Adobe postscript file of Type 1 font outlines
 *             to Wavefront object file format.
 *
 * Modified by: Wesley C. Barris
 *              AHPCRC
 *              Minnesota Supercomputer Center, Inc.
 * Date:        Dec. 5, 1990
 * Copyright @ 1990, Minnesota Supercomputer Center, Inc.
 * RESTRICTED RIGHTS LEGEND
 *
 * Use, duplication, or disclosure of this software and its documentation
 * by the Government is subject to restrictions as set forth in subdivision
 * { (b) (3) (ii) } of the Rights in Technical Data and Computer Software
 * clause at 52.227-7013.
 *
 * Original coding by Scott Gaff
 *                    Lamb & Co.
 *                    Minneapolis, MN
 *                    Nov. 30, 1987
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXLINE 80
#define MAXWORD 15
#define WORDLEN 22

FILE *obj_fp;
int  Ngrp;
float min(), max();
float xmin, xmax, ymin, ymax;

main(argc,argv)
int argc;
char *argv[];
{
   char iline[MAXLINE], arg[MAXWORD][WORDLEN];
   char *periodP, *slashP, *dashP, fname[80], base[40];
   FILE *ps_fp;
   FILE *desc_fp;
   FILE *fopen();
   int  hmarg, reset;
   int  Nv,Nelem,Nelem0;
   float Xelem0,Yelem0,X0,Y0,X1,Y1,X2,Y2,X3,Y3;
   int ncurve, EinG;

   Nelem  = 0;
   Nelem0 = 1;
   ncurve = 7;
   Ngrp   = 0;
   reset  = 1;

   if(argc != 2){
      printf("Usage: pstoobj postscript_file\n");
      exit(-1);
   }
/*
 *  Open the files.
 */
   if ((ps_fp = fopen(argv[1],"r")) == NULL) {
      fprintf(stderr,"can't open %s \n",argv[1]);
      exit (-1);
      }
   slashP = strrchr(argv[1], '/');
   dashP = strrchr(argv[1], '-');
   if (slashP) {
      if (!strncmp(slashP, "/ti", 3))
         strcpy(base, "/news");
      else if (!strncmp(slashP, "/he", 3))
         strcpy(base, "/compact");
      else if (!strncmp(slashP, "/co", 3))
         strcpy(base, "/mono");
      else if (!strncmp(slashP, "/sy", 3))
         strcpy(base, "/greek");
      }
   else {
      if (!strncmp(argv[1], "ti", 2))
         strcpy(base, "news");
      else if (!strncmp(argv[1], "he", 2))
         strcpy(base, "compact");
      else if (!strncmp(argv[1], "co", 2))
         strcpy(base, "mono");
      else if (!strncmp(argv[1], "sy", 2))
         strcpy(base, "greek");
      }
   if (dashP)
      strcat(base, dashP);
   else
      strcat(base, ".ps");
   if (slashP) {
      strcpy(fname, "OBJECTS");
      strcat(fname, base);
      }
   else {
      strcpy(fname, base);
      }
   periodP = strrchr(fname, '.');
   strcpy(periodP, ".obj");
   if ((obj_fp = fopen(fname,"w")) == NULL) {
      fprintf(stderr,"can't open obj file\n");
      exit (-1);
      }
   strcpy(periodP, ".dsc");
   if ((desc_fp = fopen(fname,"w")) == NULL) {
      fprintf(stderr,"can't open desc file\n");
      exit (-1);
      }
/*
 *  Intrepret this postscript stuff.
 */
   while (fgets(iline,sizeof(iline),ps_fp) != NULL ) {
      hmarg = get_args(iline,arg);
      if (hmarg > 0) {
/*
 *  "%%BoundingBox:" marks the bounding box of the entire char set.
 */
         if (!strcmp(arg[0],"%%BoundingBox:") ){
            if(hmarg != 5){
               fprintf(stderr,"Bounding Box error\n");
               exit(-1);
               }
            fprintf(desc_fp, "%s %s %s %s\n", arg[1], arg[2], arg[3], arg[4]);
            }
/*
 *  "*u" marks the start of a group (character).
 */
         if (!strcmp(arg[hmarg-1],"*u") ){
            if(hmarg > 1){
               fprintf(stderr,"New group error\n");
               exit(-1);
               }
            Ngrp++;
            EinG = 0;
            }
/*
 *  "0 D" marks the "outer" element of a multi-element character.
 *  NOTE: This *MUST NOT* occur in the middle of a char description.
 *  "1 D" marks the "inner" element of a multi-element character.
 *  NOTE: This *MUST NOT* occur in the beginning of a char description.
 */
         if (!strcmp(arg[hmarg-1],"D") ){
            if(hmarg != 2){
               fprintf(stderr,"Unknown element ordering thingy.\n");
               exit(-1);
               }
            if (!strcmp(arg[0],"0") ){
               if (EinG > 0) {
                  fprintf(stderr, "Bad outer element ordering in %c.  ", (char)(Ngrp+32));
                  fprintf(stderr, "Look for X values after %f\n", X0);
                  }
               }
            else if (!strcmp(arg[0],"1") ){
               if (EinG < 1) {
                  fprintf(stderr, "Bad inner element ordering in %c.  ", (char)(Ngrp+32));
                  fprintf(stderr, "Look for X values after %f\n", X0);
                  }
               }
            }
/*
 *  "f" marks the end of an element.
 */
         if (!strcmp(arg[hmarg-1],"f") ){
            if(hmarg > 1){
               fprintf(stderr,"group error\n");
               exit(-1);
               }
            if(Nelem > 0){
               if ((char)(Ngrp+32) == '\'')
                  fprintf(obj_fp, "g whole_font squote\n");
               else if ((char)(Ngrp+32) == '\"')
                  fprintf(obj_fp, "g whole_font dquote\n");
               else if ((char)(Ngrp+32) == ';')
                  fprintf(obj_fp, "g whole_font semi\n");
               else if ((char)(Ngrp+32) == '`')
                  fprintf(obj_fp, "g whole_font accent\n");
               else if ((char)(Ngrp+32) == '$')
                  fprintf(obj_fp, "g whole_font dollar\n");
               else if ((char)(Ngrp+32) == '%')
                  fprintf(obj_fp, "g whole_font percent\n");
               else if ((char)(Ngrp+32) == '\\')
                  fprintf(obj_fp, "g whole_font back_slash\n");
               else if ((char)(Ngrp+32) == ',')
                  fprintf(obj_fp, "g whole_font comma\n");
               else
                  fprintf(obj_fp, "g whole_font %c\n",(char)(Ngrp+32));
               make_elem(Nelem0,Nelem,Xelem0,Yelem0,X0,Y0);
               }       
            else {
               fprintf(stderr,"Group error 2\n");
               exit(-1);
               }
            }
/*
 *  "*U" marks the end of a group (character).
 */
         if (!strcmp(arg[hmarg-1],"*U") ){
            if(hmarg > 1){
               fprintf(stderr,"Group end error\n");
               exit(-1);
               }
            reset = 1;
            fprintf(desc_fp, "%f %f %f %f\n", xmin, ymin, xmax, ymax);
            }
/*
 *  "m" is a "moveto", an "x,y" pair is also provided.
 */
         if (!strcmp(arg[hmarg-1],"m") ){
            if(hmarg > 3){
               fprintf(stderr,"move error\n");
               exit(-1);
               }
            X0 = (float) atof(arg[0]);
            Y0 = (float) atof(arg[1]);
            fprintf(obj_fp, "v %f %f 0.0\n",X0,Y0);
            Nv = Nv + 1;
            Nelem  = 0;
            Nelem0 = Nv;
            Xelem0 = X0;
            Yelem0 = Y0;
            EinG++;
            if (reset) {
               xmin = xmax = X0;
               ymin = ymax = Y0;
               reset = 0;
               }
            else {
               xmin = min(xmin,X0);
               xmax = max(xmax,X0);
               ymin = min(ymin,Y0);
               ymax = max(ymax,Y0);
               }
            }
/*
 *  "l" or "L" is a "lineto", an "x,y" pair is also provided.
 */
         if (!strcmp(arg[hmarg-1],"l") || !strcmp(arg[hmarg-1],"L") ){
            if(hmarg > 3){
               fprintf(stderr,"line error\n");
               exit(-1);
               }
            X0 = (float) atof(arg[0]);
            Y0 = (float) atof(arg[1]);
            fprintf(obj_fp, "v %f %f 0.0\n",X0,Y0);
            Nv = Nv + 1;
            Nelem = Nelem + 1;
            xmin = min(xmin,X0);
            xmax = max(xmax,X0);
            ymin = min(ymin,Y0);
            ymax = max(ymax,Y0);
            }
/*
 *  "c" or "C" is a "curve", three control points are given.
 */
         if (!strcmp(arg[hmarg-1],"c") || !strcmp(arg[hmarg-1],"C") ){
            if(hmarg > 7){
               fprintf(stderr,"Curve error\n");
               exit(-1);
               }
            X1 = (float) atof(arg[0]);
            Y1 = (float) atof(arg[1]);
            X2 = (float) atof(arg[2]);
            Y2 = (float) atof(arg[3]);
            X3 = (float) atof(arg[4]);
            Y3 = (float) atof(arg[5]);

            curve_verts(X0,Y0,X1,Y1,X2,Y2,X3,Y3,ncurve);

            X0 = X3;
            Y0 = Y3;
            Nv = Nv + ncurve;
            Nelem = Nelem + ncurve;
            }
         }
      }
   fclose(ps_fp);
   fclose(obj_fp);
   fclose(desc_fp);
   exit(0);
}

int get_args(iline,arg)
char iline[MAXLINE];
char arg[MAXWORD][WORDLEN];
{
   char *cP;
   int ichar;
   int nargs;

   if (iline[0] == '%' && iline[2] != 'B')
      return 0;
   ichar = 0;
   nargs = 0;
   for (cP=iline; *cP != '\n'; cP++) {
      if (*cP == ' ') {
         if (nargs == MAXWORD) {
            fprintf(stderr, "Exceeded maximum number of words.");
            exit(-1);
            }
         arg[nargs][ichar] = '\0';
         nargs++;
         ichar = 0;
         }
      else {
         if (ichar == WORDLEN) {
            fprintf(stderr, "Exceeded maximum word length.");
            fprintf(stderr, "%s\n", arg[nargs]);
            exit(-1);
            }
         arg[nargs][ichar++] = *cP;
         }
      }
   arg[nargs][ichar] = '\0';
   nargs++;
   return nargs;
}

float min(a1, a2)
float a1;
float a2;
{
   if (a1 < a2)
      return a1;
   else
      return a2;
}

float max(a1, a2)
float a1;
float a2;
{
   if (a1 > a2)
      return a1;
   else
      return a2;
}

make_elem(N0,N,XX0,YY0,XX1,YY1)
int N0,N;
float XX0,YY0,XX1,YY1;
{
   int i, j;
   int NNN;

   NNN = 0;
   fprintf(obj_fp, "f");

/*------------backwards for counter clock-wise---------------*/

   for(i = N0 + N ;i > N0;i--){
      NNN++;
      if (NNN <= 400)
         fprintf(obj_fp, " %d",i);
      else {
         fprintf(obj_fp, "\n");
         /*printf("g %s char%d overflow\n",filename,Ngrp);*/
         if ((char)(Ngrp+32) == '\'')
            fprintf(obj_fp, "g whole_font squote\n");
         else if ((char)(Ngrp+32) == '\"')
            fprintf(obj_fp, "g whole_font dquote\n");
         else if ((char)(Ngrp+32) == ';')
            fprintf(obj_fp, "g whole_font semi\n");
         else if ((char)(Ngrp+32) == '`')
            fprintf(obj_fp, "g whole_font accent\n");
         else
            fprintf(obj_fp, "g whole_font %c\n",(char)(Ngrp+32));
         fprintf(stderr, "Overflow\n");
         fprintf(obj_fp, "f");
         for (j = i+1; j > N0; j--)
            fprintf(obj_fp, " %d",j);
         fprintf(obj_fp, "\n");
         NNN = 0;
         i = j;
         }
   }

/*------------check if last vertex is equal to the first--------------------*/
/*------------if so reference the first in the element----------------------*/
/*
 *  I have to make the last and first vertice the same if I'm going to
 *  use lines.  Otherwize, gaps will appear.
 */

   if((XX0 == XX1) && (YY0 == YY1)){
      /*fprintf(obj_fp, " %d",N0 + N);*/
      }
   else
      fprintf(obj_fp, " %d",N0);
   fprintf(obj_fp, "\n");
   return(0);
}

curve_verts(X0,Y0,X1,Y1,X2,Y2,X3,Y3,ncurve)
float X0,Y0,X1,Y1,X2,Y2,X3,Y3;
int ncurve;
{
int i;
float ax,bx,cx;
float ay,by,cy;
float X,Y;
float t,t2,t3,tdelta;

   cx =  3.0 * (X1 - X0);
   bx = (3.0 * (X2 - X1)) - cx;
   ax =         X3 - X0 - cx - bx;
   cy =  3.0 * (Y1 - Y0);
   by = (3.0 * (Y2 - Y1)) - cy;
   ay =         Y3 - Y0 - cy - by;

   tdelta = 1.0 / (float)ncurve;
   t = 0.0;

   for(i = 1 ;i < ncurve ;i = i +1){
      t = t + tdelta;
      t2 = t * t;
      t3 = t * t * t;
      X = (ax * t3) + (bx * t2) + (cx * t) + X0;
      Y = (ay * t3) + (by * t2) + (cy * t) + Y0;
      fprintf(obj_fp, "v %f %f 0.0\n",X,Y);
      xmin = min(xmin,X);
      xmax = max(xmax,X);
      ymin = min(ymin,Y);
      ymax = max(ymax,Y);
      }
   fprintf(obj_fp, "v %f %f 0.0\n",X3,Y3);
   xmin = min(xmin,X);
   xmax = max(xmax,X);
   ymin = min(ymin,Y);
   ymax = max(ymax,Y);

   return(0);
}
