 /*
    -----------------------------------------------------------------------------
 Original:
    txt2pdf; Copyright 1998
    P. G. Womack, Diss, Norfolk, UK.
    "BugBear"
    Do what you like, but don't claim you wrote it.
    -----------------------------------------------------------------------------
     *
     * Copyright (c) 2006 John S. Urban, USA. (urbanjost @ comcast. net)
     * All rights reserved.
     * 
     * Permission to use, copy, modify and distribute this software and
     * its documentation is hereby granted, provided that both the copyright
     * notice and this permission notice appear in all copies of the
     * software, derivative works or modified versions, and any portions
     * thereof, and that both notices appear in supporting documentation.
     * 
     * JOHN S. URBAN ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 
     * CONDITION.  JOHN S. URBAN DISCLAIMS ANY LIABILITY OF ANY KIND 
     * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     * 
     * John S. Urban requests users of this software to return to
     *
     *  urbanjost @ comcast. net            
     *
     * any improvements or extensions that they make and grant John S. Urban and the
     * previous authors (see above ) the rights to redistribute these changes.
     * 
    -----------------------------------------------------------------------------
 Modified:
    asa2pdf; John S. Urban, Apr 30, 2006
    Needed to emulate an old ASA 60 line by 132 column lineprinter quicky with
    output as a PDF file.  Thanks, P.G.W.!
 
     o Added bar shading.
     o placed Adobe-recommended "binary" flag at top of PDF file.
     o changed sizes to simulate a 60 line by 132 column lineprinter.
     o changed so obeys ASA carriage control (' 01+' in column 1).
     o user-settable gray scale value via environmental variable IMPACT_GRAY

    -----------------------------------------------------------------------------
    Next time in:
 
     o user-specified lines and columns and page orientation             (T  0.4)
     o expand tabs (can use expand(1))                                   (T  0.1)
     o command line options instead of environmental variables?          (T  0.2)
     o optional lines instead of shade; dash lines; shade spacing        (T  0.5)
     o 1/2 line feeds?                                                   (T  0.2)
     o bold Courier font on overprint?                                   (T  0.3)
     o underline? font color? shade color? font size?                    (?  3.0)
 
     But, if someone wants to get that fancy they can use
     fpr(1)/asa(1)/nasa(1) to handle the ASA carriage control, and
     something like a2ps(1)/enscript(1) and ps2pdf(1) if they have them
     or build them for their system.                                     (?  0.0 -- 10.0)
    -----------------------------------------------------------------------------
 
     Tested with xpdf, gv/ghostview, and acroread (PC version) PDF interpreters.
 
    -----------------------------------------------------------------------------
 */
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 
 float page_depth;
 float page_width;  /* Default is 72 points per inch */
 float margin;
 float lead_size;
 float font_size;
 int object_id = 1;
 int page_tree_id;
 float gray_scale;
 
 typedef struct _PageList {
    
    struct _PageList *next;
    
        int page_id;
    
 } PageList;
 
 
 int num_pages = 0;
 PageList *pages = NULL;
 PageList **insert_page = &pages;
 
 void store_page(int id){
    
        PageList *n = (PageList *)malloc(sizeof(*n));
    
        if(n == NULL)
    {
           fprintf(stderr, 
           "Unable to allocate array for page %d.", num_pages + 1);
           exit(1);
    }
        n->next = NULL;
        n->page_id = id;
        *insert_page = n;
        insert_page = &n->next;
        num_pages++;
    
 }
 
 int num_xrefs = 0;
 long *xrefs = NULL;
 
 void start_object(int id){
        if(id >= num_xrefs) {
 
           long *new_xrefs;
           int delta, new_num_xrefs;
           delta = num_xrefs / 5;
       
           if(delta < 1000) {
              delta += 1000;
 	  }
       
           new_num_xrefs = num_xrefs + delta;
           new_xrefs = (long *)malloc(new_num_xrefs * sizeof(*new_xrefs));
 
           if(new_xrefs == NULL) {
              fprintf(stderr, "Unable to allocate array for object %d.", id);
              exit(1);
           }
       
           memcpy(new_xrefs, xrefs, num_xrefs * sizeof(*xrefs));
           free(xrefs);
           xrefs = new_xrefs;
           num_xrefs = new_num_xrefs;
       
        }
    
        xrefs[id] = ftell(stdout);
        printf("%d 0 obj\n", id);
    
 }
 
 
 int stream_id, stream_len_id;
 long stream_start;
 float ypos;
 
 void print_bars(){
    
        float x1;
        float y1;
        float height;
        float width;
        int ii;
    
        fprintf(stdout,"%f g\n",gray_scale); /* gray-scale value */
        /*
 	* If you want to add color, 
 	* R G B rg where R G B are red, green, blue components 
 	* in range 0.0 to 1.0 sets fill color, "RG" sets line
 	* color instead of fill color.
 	*
 	* 0.60 0.82 0.60 rg
 	*
 	* */
 
        fprintf(stdout,"%d i\n",1); /*  */
    
        x1=margin-0.1*font_size;
        height=2.0*lead_size;
        y1 = page_depth - margin - height- 0.22*font_size;
        width=page_width-margin-margin;
    
        for ( ii=0; ii < 15; ii++ ){
           fprintf(stdout,"%f %f %f %f re f\n",x1,y1,width,height);
 	  /*
 	   * x1 y1 m x2 y2 l S
 	   * xxx w  # line width
           fprintf(stdout,"0.6 0.8 0.6 RG\n %f %f m %f %f l S\n",x1,y1,x1+width,y1);
 	   */
           y1=y1-2.0*height;
        }
    
        fprintf(stdout,"%d G\n",0); /* */
        fprintf(stdout,"%d g\n",0); /* gray-scale value */
    
 }

 void print_back(){
        char *buffer;
        char c;
	int i;

	buffer=getenv("IMPACT_TOP");
	if(buffer != NULL ){
              fprintf(stdout,"1.0 0.0 0.0 rg\n"); /* gray-scale value */
              fprintf(stdout,"BT /F2 20.0 Tf 30 582 Td");
 	      i=0;
              putchar('(');
              while((c = *buffer++) != '\0') {
                    switch(c) {
                       case '(':
                       case ')':
                       case '\\':
                          putchar('\\');
                    }
                    putchar(c);
              }
        fprintf(stdout,") Tj ET\n"); 
        fprintf(stdout,"0.0 0.0 0.0 rg\n"); /* gray-scale value */
	}
 }
 
 void start_page() {
    
        stream_id = object_id++;
        stream_len_id = object_id++;
        start_object(stream_id);
        printf("<< /Length %d 0 R >>\n", stream_len_id);
        printf("stream\n");
        stream_start = ftell(stdout);
        print_bars();
	print_back();
        printf("BT\n/F0 %g Tf\n", font_size);
        ypos = page_depth - margin;
        printf("%g %g Td\n", margin, ypos);
        printf("%g TL\n", lead_size);
    
 }
 
 
 void end_page(){
    
        long stream_len;
        int page_id = object_id++;
    
        store_page(page_id);
        printf("ET\n");
        stream_len = ftell(stdout) - stream_start;
        printf("endstream\nendobj\n");
        start_object(stream_len_id);
        printf("%ld\nendobj\n", stream_len);
        start_object(page_id);
        printf("<</Type/Page/Parent %d 0 R/Contents %d 0 R>>\nendobj\n", page_tree_id, stream_id);
    
 }
 
 
 void do_text() {
    
        char buffer[8192];
        char ASA;
        char c;
        char *s;
	int black;
	int add;
    
        start_page();

    
        while(gets(buffer) != NULL) {
 
	   black=0;
	   add=0;

           if(ypos < margin && strlen(buffer) != 0 && buffer[0] != '+' ) {
              end_page();
              start_page();
            }
       
           if(strlen(buffer) == 0){ /* blank line */
              printf("T*\n");
 	  }else{
 	     ASA=buffer[0];
 
              switch(ASA) {
 
               case '1':      	/* start a new page before processing data on line */
 		 if (ypos < page_depth - margin ){
                     end_page();
                     start_page();
 		 }
 	       break;
 
               case '0':      	/* put out a blank line before processing data on line */
                  printf("T*\n");
                  ypos -= lead_size;
 	       break;
 
               case '-':      	/* put out two blank lines before processing data on line */
                  printf("T*\n");
                  ypos -= lead_size;
                  ypos -= lead_size;
 	       break;
 
               case '+':     	/* print at same y-position as previous line */
                  printf("0 %f Td\n",lead_size);
 		  if (ypos < page_depth - margin ){  /* if not at top of page */
                     ypos += lead_size;
 		  }
 	       break;
               case 'R':     	/* RED print at same y-position as previous line */
                  fprintf(stdout,"1.0 0.0 0.0 rg\n"); /* red text */
		  black=1;
                  printf("0 %f Td\n",lead_size);
 		  if (ypos < page_depth - margin ){  /* if not at top of page */
                     ypos += lead_size;
 		  }
 	       break;
               case 'G':     	/* GREEN print at same y-position as previous line */
                  fprintf(stdout,"0.0 1.0 0.0 rg\n"); /* green text */
		  black=1;
                  printf("0 %f Td\n",lead_size);
 		  if (ypos < page_depth - margin ){  /* if not at top of page */
                     ypos += lead_size;
 		  }
 	       break;
               case 'B':     	/* BLUE print at same y-position as previous line */
                  fprintf(stdout,"0.0 0.0 1.0 rg\n"); /* blue text */
		  black=1;
                  printf("0 %f Td\n",lead_size);
 		  if (ypos < page_depth - margin ){  /* if not at top of page */
                     ypos += lead_size;
 		  }
 	       break;

               case '^':     	/* print at same y-position as previous line like + but add 127 to character */
                  printf("0 %f Td\n",lead_size);
 		  if (ypos < page_depth - margin ){  /* if not at top of page */
                     ypos += lead_size;
 		  }
		  add=127;
 	       break;
 
               case '>':     	/* Unknown */
 	       break;
 
               case '\f':       /* ctrl-L is a common form-feed character on Unix, but NOT ASA */
                  end_page();
                  start_page();
 	       break;
 
               case 'r':
                  fprintf(stdout,"1.0 0.0 0.0 rg\n"); /* red text */
		  black=1;
 	       break;
               case 'g':
                  fprintf(stdout,"0.0 1.0 0.0 rg\n"); /* green text */
		  black=1;
 	       break;
               case 'b':
                  fprintf(stdout,"0.0 0.0 1.0 rg\n"); /* blue text */
		  black=1;
 	       break;
               case ' ':
 	       break;
 
                default:
 	         fprintf(stderr,"unknown ASA carriage control character %c\n",buffer[0]);
 	       break;
 
              }
 
 	     s = &buffer[1];
              putchar('(');
              while((c = *s++) != '\0') {
                    switch(c+add) {
                       case '(':
                       case ')':
                       case '\\':
                          putchar('\\');
                    }
                    putchar(c+add);
              }
              printf(")'\n");
             
           }
           ypos -= lead_size;
	   if(black != 0){
                  fprintf(stdout,"0.0 0.0 0.0 rg\n"); /* black text */
	   }
       
        }
        end_page();
 }
 
 int main(int argc, char **argv) {
    
        int i, catalog_id, font_id;
        long start_xref;
        char *varname;
 
        varname=getenv("IMPACT_GRAY");
        if (varname == (char)NULL ){
            gray_scale=0.800781; /* gray-scale value */
	}else if (varname[0] == '\0'){
            gray_scale=0.800781; /* gray-scale value */
        }else{
 	   sscanf(varname,"%f",&gray_scale);
 	   if(gray_scale < 0 ){
               gray_scale=0.800781; /* gray-scale value */
 	   }
        }
 
    
        printf("%%PDF-1.0\n");
    
        /*
           Note: If a PDF file contains binary data, as most do , it is
           recommended that the header line be immediately followed by a
           comment line containing at least four binary characters--that is,
           characters whose codes are 128 or greater. This will ensure proper behavior of file
           transfer applications that inspect data near the beginning of a
           file to determine whether to treat the file's contents as text or as binary.
        */
        fprintf(stdout,"%%%c%c%c%c\n",128,129,130,131);
        fprintf(stdout,"%% PDF: Adobe Portable Document Format\n");
    
        page_depth = 612.0;
        page_width = 792.0;  /* Default is 72 points per inch */
        margin = 30.0;
        lead_size=(page_depth-margin-margin)/60.0;
        font_size=lead_size;
        object_id = 1;
        page_tree_id = object_id++;
        do_text();
        font_id = object_id++;
        start_object(font_id);
        printf("<</Type/Font/Subtype/Type1/BaseFont/Courier/Encoding/WinAnsiEncoding>>\nendobj\n");
        start_object(page_tree_id);
    
        printf("<</Type /Pages /Count %d\n", num_pages);
    
        {
           PageList *ptr = pages;
           printf("/Kids[\n");
           while(ptr != NULL) {
              printf("%d 0 R\n", ptr->page_id);
              ptr = ptr->next;
           }
           printf("]\n");
        }
    
        printf("/Resources<</ProcSet[/PDF/Text]/Font<</F0 %d 0 R\n", font_id);
        printf(" /F2<</Type/Font/Subtype/Type1/BaseFont/Helvetica/Encoding/WinAnsiEncoding >> >>\n");
        printf(">>/MediaBox [ 0 0 %g %g ]\n", page_width, page_depth);
        printf(">>\nendobj\n");
        catalog_id = object_id++;
        start_object(catalog_id);
        printf("<</Type/Catalog/Pages %d 0 R>>\nendobj\n", page_tree_id);
        start_xref = ftell(stdout);
        printf("xref\n");
        printf("0 %d\n", object_id);
        printf("0000000000 65535 f \n");
    
        for(i = 1; i < object_id; i++){
           printf("%010ld 00000 n \n", xrefs[i]);
        }
    
        printf("trailer\n<<\n/Size %d\n/Root %d 0 R\n>>\n", object_id, catalog_id);
        printf("startxref\n%ld\n%%%%EOF\n", start_xref);
    
        exit(0);
    
        return 0;
 }
