/*
  Draw2EPS

  By Peter Hillman

  Copyright Fri 17th March 2000 Square Eyes Software

  v0.00 2000-03-17 PH
  v0.01 2006-02-08 EP added a semi-colon at end of switch statement that was
         causing failure to compile with GCC v3.4.4 with error 'label at end
         of compound statement'.
         Changed return type of main() from void to int, returning a value of
         0, again to allow compilation with GCC v3.4.4.
         New line included at end of EPS file before EOF reading 'showpage' to
         make it open in GView using GhostScript for RISC OS.
         Compiled to 32bit with GCC v3.4.4 and tested on an Iyonix with
         RISC OS 5.10.
  v0.02 2013-08-01 EP Re-compiled with GCC v4.1.2 release 2 for RISC OS,
         as previous version crashed on Beagleboard. It is now an ELF filetype,
         not absolute. Probably requires !SharedLibs in !Boot.resources.
         Tested with RISC OS 5.19.
         Changed creator string in output to remove out of date email address
         and include utility version number and date.

*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


/*
 * read a little endian 32bit word from the given buffer,
 * and return the word. pos will point to the end of
 * the buffer on exit
 */

unsigned int getint(unsigned char ** pos){
  unsigned int t=0;
  int l;
  for(l=0;l<4;l++){
    t=t|((**pos)<<(l*8));
    (*pos)++;
  }
  return t;
}

/*
 * print out 'n' integers
 */

void printn(unsigned char **pos,int n){
  for(;n>0;n--){
    printf("%d ",getint(pos));
  }
}

/*handle text (tran==0) or tranformed text (tran==1) objects*/


void dotext(unsigned char * data,int tran){
  int a=0,b=0,c=0,d=0,tx=0,ty=0; /*transformation matrix*/
  unsigned int color;         /*text color*/
  unsigned int backg,style,bits; /*ignored*/
  unsigned int xsize,ysize;  /*size of text*/
  unsigned int xco,yco;      /*position of text*/



  if(tran==1){
    a=getint(&data);
    b=getint(&data);
    c=getint(&data);
    d=getint(&data);
    tx=getint(&data);
    ty=getint(&data);
    bits=getint(&data);
  }
  color=getint(&data);
  backg=getint(&data);
  style=getint(&data);
  xsize=getint(&data);
  ysize=getint(&data);
  xco=getint(&data);
  yco=getint(&data);

  /*get a font of the right size*/

  printf("\n\n/Times-Roman findfont\n");
  printf("%d scalefont\n",xsize);
  printf("setfont\n");

  /* move to the right place*/
  printf("newpath\n%d %d moveto\n",xco,yco);

  /*add transformation matrix*/
  if(tran==1){
    printf("gsave\n[%3.3f %3.3f %3.3f %3.3f %3.3f %3.3f] concat\n",(float) a/65536.0,
     (float) b/65536.0,     (float) c/65536.0,     (float) d/65536.0,
     (float) tx/65536.0,     (float) ty/65536.0
    );
  }
  if(ysize!=xsize && xsize>=0){
    printf("gsave\n1 %f scale\n",(float) ysize/ (float) xsize);
  }
  printf("(%s) show\n",data);
  if(ysize!=xsize && xsize>=0){
    printf("grestore\n");
  }
  if(tran==1){
    printf("grestore\n");
  }
}

/*
 * this actually processes a path
 * if t==0, produces a filled object
 * if t==1, prduced a stroked ovject
 * Drawfile has an object which can be
 * filled in one colour and stroked in another.
 * Postscript cannot, so two objects are produced
 */

void processpath(unsigned char * path,unsigned char * end,int t){
  unsigned int  fill=getint(&path);
  unsigned int licol=getint(&path);
  unsigned int width=getint(&path);
  unsigned int style=getint(&path);
  unsigned int offset;
  unsigned int dashelements;
  int ended=0,closed=0;
  unsigned int color;

  /*
   * check to see if we should be doing anything
   */

  if(t==0 && fill==0xFFFFFFFF){
    return;
    /*
     * we were meant to be doing the filled part,
     * but this isn't filled, so give up.
     */
  }

  if(t==1 && licol==0xFFFFFFFF){
    return;
    /*
     * we were meant to be doing the stroked part,
     * but this isn't stroked, so give up.
     */

  }

 /*finally, process the path*/

  printf("%d setlinewidth\n",width);

  /*build the color*/
  color=t ? licol : fill;

  printf("%4.4f %4.4f %4.4f setrgbcolor\n", ((color>> 8) & 255 ) / 255.0,
                                   ((color>>16) & 255 ) / 255.0,
                                   ((color>>24) & 255 ) / 255.0
        );

  printf("\n\nnewpath\n");


  /*join style*/

  printf("%d setlinejoin\n",style&3);

  /*end cap*/

  if( ( (style&(3<<2)) >> 2 ) < 3 ){
    printf("%d setlinecap\n",(style&(3<<2))>>2);
  }

  /*start cap*/

  switch(( style&(3<<4))>>4){
    case 0 : /*butt*/
    case 1 : /*round*/
    case 2 : /*square*/
    case 3 :; /*triangle*/
  }

  /*bits 16-23*: triangle cap width*/
  /*bits 24-31*: triangle cap length*/

  /*dash pattern*/
  switch(( style&(1<<7))>>7){
    case 0 : /*there's no dash pattern! Wahey!*/
      printf("[] 0 setdash\n");
      break;
    case 1 : /*there's a dash. shit*/
      offset=getint(&path); /*'offset to where to start'*/
      dashelements=getint(&path);
      printf("[ ");
      printn(&path,dashelements);
      printf("] %d setdash\n",offset);
  }

 /*process all the types of line whatsits*/

  while(path<end){
    unsigned int tag=getint(&path);
    switch(tag){
      case 0 : ended=1;break;
      case 5 : printf(" closepath\n");closed=1;break;
      case 2 : printn(&path,2);printf(" moveto\n");closed=0;break;
      case 8 : printn(&path,2);printf(" lineto\n");break;
      case 6 : printn(&path,6);printf(" curveto\n");
    }
  }

  /*check to see if we ended OK*/

  if(ended!=1){
    fprintf(stderr,"# memory ran out before path ended!\n");
  }

  /*should we stroke, fill or eofill?*/
 if(t){
   printf("stroke\n");
 }else{
      if(!closed) printf("closepath\n");
     /*winding rule*/
      switch(( style&(1<<6))>>6){
        case 0 :   printf("fill\n");
        case 1 : printf("eofill\n");
      }
  }
}


int main(int argc,char * argv[])
{
  unsigned char * buffer;               /* um file*/
  int length;                          /* length of um file*/
  int got;
  unsigned char * readpos;      /* um pointer to um place to read from*/
  int objtype,objlength;       /* um type and um length of um current obj*/
  FILE * f;

  /*
   * step one, check arguments and open up file.
   * report error if it don't work
   */

  if( argc!=2 || !(f=fopen(argv[1],"r"))  )
  {
    if(argc==2)  fprintf(stderr,"Couldn't open file %s\n",argv[1]);
    fprintf(stderr,"Draw2EPS converts a drawfile into a simple EPS file, "
                   "which is output to stdio\nSyntax: %s <input drawfile>\n"
                   ,argv[0]);
    exit(0);
  }


  /*
   * step two, find out how big the file is
   */

  fseek(f,0,SEEK_END);
  length=ftell(f);
  rewind(f);


  /*
   * now, have a bash at loading in the file
   * tolerate allocation failure and failure to load whole file
   */

  buffer = (unsigned char *) calloc( sizeof(unsigned char) , length );

  if(buffer==NULL)
  {
    fprintf(stderr,"Exiting: couldn't allocate %d bytes to load file %s\n",
                   length,argv[1]);
    exit(0);
  }


  if( length != (got=fread(buffer,sizeof(unsigned char),length,f)) )
  {
    fprintf(stderr,"Exiting: loaded only %d bytes of %d  file %s\n",
                   got,length,argv[1]);
    exit(0);
  }


  /*
   * Now we have the file, let's check that
   * it's a drawfile
   */

  if(strncmp("Draw",(char *) buffer,4)){
    fprintf(stderr,"Exiting: %s isn't a drawfile\n",argv[1]);
    exit(0);
  }

  /*
   * Print out the standard EPS header
   */

   printf("%%!PS-Adobe-2.0 EPSF-1.2\n");
   printf("%%%%Title: %s\n",argv[1]);
   printf("%%%%Creator: Draw2EPS RISC OS utility v0.02\n");
   printf("%%%%Pages: 1\n");
   printf("%%%%DocumentFonts:\n");

  /*
   * Now, let's find out how big the bounding box is
   * and print it out
   */

   readpos=buffer+24;
   printf("%%%%BoundingBox: ");
   printf("%d ",getint(&readpos)*72/(512*90));
   printf("%d ",getint(&readpos)*72/(512*90));
   printf("%d ",getint(&readpos)*72/(512*90));
   printf("%d\n",getint(&readpos)*72/(512*90));

   printf("%%%%EndComments\n");
   printf("%%BeginProlog\n");
   printf("save\ncountdictstack\nmark\nnewpath\n");
   printf("%%%%EndProlog\n");
   printf("%%%%BeginSetup\n");
   printf("%%%%EndSetup\n");
   printf("%%%%Page: 1 1\n");
   printf("%%%%BeginPageSetup\n");
   printf("%%%%EndPageSetup\n");

   /*
    * drawfiles are in points (72nd of an inch) but drawfiles are
    * 512ths of an OS unit
    */
   printf("gsave\n");
   printf("72 90 512 mul div dup scale\n");

   /*
    * now let's do the rest of the file!!!
    */

   while(readpos<buffer+length)
   {/*until readpos is past the end of the file*/

       objtype=getint(&readpos);
     objlength=getint(&readpos);
     switch(objtype){
       case 0 : /*font table : IGNORED */
          readpos+=objlength-8;
          break;
       case 1 : /*text object*/
          dotext(readpos+16,0);
          readpos+=objlength-8;
          break;
       case 2 : /*path object*/
          processpath(readpos+16,objlength+readpos-8,0);
          processpath(readpos+16,objlength+readpos-8,1);
          readpos+=objlength-8;
          break;
       case 5 : /*sprite object: IGNORED */
          readpos+=objlength-8;
          break;
       case 6 : /*group object*/
          readpos+=28;
          break;
       case 7 : /*tagged object: IGNORED*/
       case 9  : /*text area object: IGNORED*/
       case 10 : /*text column object: IGNORED*/
       case 11 : /*options object: IGNORED*/
         readpos+=objlength-8;
          break;
       case 12 : /*Transformed text object*/
          dotext(readpos+16,1);
         readpos+=objlength-8;
          break;
       case 13 : /*Transformed sprite object: IGNORED*/
       case 16 : /*JPEG: Ignored*/
       default : /*just in case there's more*/
         readpos+=objlength-8;
          break;
     }
   }
   printf("\n\n%%%%%%drawfileend\ngrestore\n");
   printf("%%%%PageTrailer\n%%%%Trailer\ncleartomark\n");
   printf("countdictstack exch sub { end } repeat\n");
   printf("showpage\n");
   printf("%%%%EOF\n");

   return 0;
}
/*
 It is better to have loved and lost, than never have loved at all
 */
