
/******************************************************************************
* MODULE     : printer.gen.cc
* DESCRIPTION: Abstract device for printing post-script graphics
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#include <PsDevice/printer.gen.h>
#include <file.gen.h>
#include <analyze.gen.h>
#include <iterator.gen.h>
#include <merge_sort.gen.cc>

#module code_printer
#import printer
#import file
#import analyze
#import iterator (string)
#import hashmap_iterator (string, string)

#import code_merge_sort (int)

string PS_LINE ("ln");
string PS_FILL ("fl");
string PS_ARC ("ac");
string PS1 ("u");
string PS2 ("z");

/******************************************************************************
* constructors and destructors
******************************************************************************/

printer_rep::printer_rep (
  display dis2, string ps_file_name2, int dpi2, int nr_pages2,
  string page_type2, bool landscape2, double paper_w2, double paper_h2):
    dis (dis2), ps_file_name (ps_file_name2), dpi (dpi2),
    nr_pages (nr_pages2), page_type (page_type2),
    landscape (landscape2), paper_w (paper_w2), paper_h (paper_h2),
    linelen (0), fg (-1), bg (-1), ncols (0),
    lw (-1), nwidths (0), cfn (""), nfonts (0),
    xpos (0), ypos (0), tex_flag (FALSE), defs ("?"), tex_chars ("?"),
    tex_fonts ("?"), tex_font_chars (array<int>(0))    
{
  black     = dis->black;
  white     = dis->white;
  red       = dis->red;
  green     = dis->green;
  blue      = dis->blue;
  yellow    = dis->yellow;
  magenta   = dis->magenta;
  orange    = dis->orange;
  brown     = dis->brown;
  pink      = dis->pink;
  light_grey= dis->light_grey;
  grey      = dis->grey;
  dark_grey = dis->dark_grey;

  string tex_pro, special_pro, color_pro;
  load_string ("$TEXMACS_PATH/misc/convert/tex.pro", tex_pro, TRUE);
  load_string ("$TEXMACS_PATH/misc/convert/special.pro", special_pro, TRUE);
  load_string ("$TEXMACS_PATH/misc/convert/color.pro", color_pro, TRUE);
  set_clipping (0, (int) (-(dpi*PIXEL*paper_h)/2.54),
		(int) ((dpi*PIXEL*paper_w)/2.54), 0);
  
  prologue   << "%!PS-Adobe-2.0\n"
	     << "%%Creator: TeXmacs Version 1.0.0.1\n"
	     << "%%Title: " << ps_file_name << "\n"
	     << "%%Pages: " << as_string (nr_pages) << "\n"
	     << "%%PageOrder: Ascend\n";
  if (page_type != "user")
    prologue << "%%DocumentPaperSizes: " << page_type << "\n";
  if (landscape)
    prologue << "%%BoundingBox: 0 0 "
	     << as_string ((int) (28.36*paper_h+ 0.5)) << " "
	     << as_string ((int) (28.36*paper_w+ 0.5)) << "\n"
	     << "%%Orientation: Landscape\n";
  else
    prologue << "%%BoundingBox: 0 0 "
	     << as_string ((int) (28.36*paper_w+ 0.5)) << " "
	     << as_string ((int) (28.36*paper_h+ 0.5)) << "\n";
  prologue   << "%%EndComments\n\n"
	     << tex_pro << "\n"
	     << special_pro << "\n"
	     << "TeXDict begin\n"
	     << as_string ((int) (1864680.0*paper_w+ 0.5)) << " "
	     << as_string ((int) (1864680.0*paper_h+ 0.5)) << " 1000 "
	     << as_string (dpi) << " " << as_string (dpi)
	     << " (TeXmacs) @start\n";

  define (PS_LINE, string ("/pt4 X /pt3 X /pt2 X /pt1 X\n") *
	  string ("newpath pt1 pt2 moveto pt3 pt4 lineto stroke"));
  define (PS_FILL, string ("/pt4 X /pt3 X /pt2 X /pt1 X\n") *
	  string ("newpath pt1 pt2 moveto pt3 pt2 lineto ") *
	  string ("pt3 pt4 lineto pt1 pt4 lineto pt1 pt2 eofill stroke"));
  define (PS_ARC, string ("/a2 X /a1 X /r2 X /r1 X /pt2 X /pt1 X\n") *
	  string ("newpath pt1 pt2 r1 r2 a1 a2 ellipse stroke"));
  define (PS1, string ("gsave"));
  define (PS2, string ("1 -1 scale show grestore"));

  cur_page= 0;
  next_page ();
}

printer_rep::~printer_rep () {
  next_page ();
  body << "\n%%Trailer\n"
       << "end\n"
       << "userdict /end-hook known{end-hook} if\n"
       << "%%EOF\n";

  generate_tex_fonts ();
  prologue << "end\n"
           << "%%EndProlog\n\n"
	   << "%%BeginSetup\n"
	   << "%%Feature: *Resolution " << as_string (dpi) << "dpi\n"
	   << "TeXDict begin\n";
  if (page_type != "user")
    prologue << "%%PaperSize: " << page_type << "\n";
  if (landscape)
    prologue << "@landscape\n";
  prologue << "%%EndSetup\n";

  string ps_text= prologue * "\n" * body;
  save_string (ps_file_name, ps_text);
}

/******************************************************************************
* subroutines for printing
******************************************************************************/

void
printer_rep::next_page () {
  if (cur_page > 0) print ("eop\n");
  if (cur_page >= nr_pages) return;
  cur_page++;
  body << "\n%%Page: " << as_string (cur_page) << " "
       << as_string (nr_pages) << "\n"
       << as_string (cur_page) << " "
       << as_string (cur_page-1) << " bop\n";

  fg  = -1;
  bg  = -1;
  lw  = -1;
  cfn = "";
  xpos= 0;
  ypos= 0;
 }

void
printer_rep::define (string s, string defn) {
  if (defs->contains (s)) return;
  defs (defn)= s;
  prologue << "/" << s << " {" << defn << "} N\n";
}

void
printer_rep::sep () {
  if ((N(body) > 0) &&
      (body [N(body)-1] != ')') &&
      (body [N(body)-1] != '\n')) {
    body << " ";
    linelen++;
    tex_flag= FALSE;
  }
}

void
printer_rep::cr () {
  body << "\n";
  linelen= 0;
  tex_flag= FALSE;
}

void
printer_rep::print (string s) {
  if (N(s)==0) return;
  if ((linelen>0) && (linelen+N(s)>79)) {
    body << "\n";
    linelen= 0;
    tex_flag= FALSE;
  }
  else if (s[0]!='(') sep ();
  if (tex_flag && (s[0]=='(')) {
    body->resize (N(body)-2);
    linelen -= 2;
    s= s (1,N(s));
  }
  body << s;
  linelen += N(s);
  tex_flag= FALSE;
}

void
printer_rep::print (SI x, SI y) {
  x += ox; y += oy;
  if (x>=0) x= x/PIXEL; else x= (x-PIXEL+1)/PIXEL;
  if (y>=0) y= y/PIXEL; else y= (y-PIXEL+1)/PIXEL;
  print (as_string (x-dpi));
  print (as_string (-y-dpi));
}

void
printer_rep::move_to (SI x, SI y) {
  x += ox; y += oy;
  if (x>=0) x= x/PIXEL; else x= (x-PIXEL+1)/PIXEL;
  if (y>=0) y= y/PIXEL; else y= (y-PIXEL+1)/PIXEL;
  if (tex_flag && (xpos==x) && (ypos==y)) return;
  if (tex_flag && (ypos==y)) {
    body->resize (N(body)-1);
    linelen -= 1;
    tex_flag= FALSE;

    int diff= x-xpos;
    if ((diff>=-4) && (diff<=4)) print (string ((char) ('p'+diff)));
    else {
      print (as_string (diff));
      print ("b");
    }
    xpos= x;
    return;
  }
  xpos= x; ypos= y;
  print (as_string (x-dpi));
  print (as_string (-y-dpi));
  print ("a");
}

void
printer_rep::select_color (color c) {
  int r, g, b;
  dis->get_rgb (c, r, g, b);
  r= 10000+ ((r*1000)/255);
  g= 10000+ ((g*1000)/255);
  b= 10000+ ((b*1000)/255);
  string rr= as_string (r); rr= rr(1,2) * "." * rr(2,5);
  string gg= as_string (g); gg= gg(1,2) * "." * gg(2,5);
  string bb= as_string (b); bb= bb(1,2) * "." * bb(2,5);
  string s = rr * " " * gg * " " * bb * " setrgbcolor";
  if (!defs->contains (s)) {
    define ("C" * as_string (ncols), s);
    ncols++;
  }
  print (defs[s]);
}

void
printer_rep::select_line_width (SI w) {
  w= w/PIXEL; if (w<1) w=1;
  string s = as_string (w) * " setlinewidth";
  if (!defs->contains (s)) {
    define ("W" * as_string (nwidths), s);
    nwidths++;
  }
  print (defs[s]);
}

/******************************************************************************
* subroutines for fonts
******************************************************************************/

static string
prepare_text (string s) {
  int i;
  string r;
  for (i=0; i<N(s); i++) {
    int c= ((unsigned char) s[i]);
    if ((s[i]=='(') || (s[i]==')') || (s[i]=='\\'))
      r << '\\' << s[i];
    else if ((c <= 32) || (c >= 128)) {
      r << '\\';
      r << ('0' + (c >> 6));
      r << ('0' + ((c >> 3) & 7));
      r << ('0' + (c & 7));
    }
    else r << s[i];
  }
  return r;
}

void
printer_rep::select_tex_font (string name) {
  if (cfn==name) return;
  cfn= name;
  print (tex_fonts [name]);
}

/******************************************************************************
* make TeX character
******************************************************************************/

static char* hex_string= "0123456789ABCDEF";

static string
as_hexadecimal (int i, int len=2) {
  if (len==1) return hex_string [i & 15];
  else return as_hexadecimal (i >> 4, len-1) * hex_string [i & 15];
}

void
printer_rep::make_tex_char (string name, unsigned char c, bitmap_char bmc) {
  string char_name (name * "-" * as_string ((int) c));
  if (tex_chars->contains (char_name)) return;
  if (!tex_fonts->contains (name)) {
    tex_fonts (name)= "F" * as_string (nfonts);
    tex_font_chars (name)= array<int> (0);
    nfonts++;
  }
  tex_font_chars (name) << ((int) c);

  string hex_code;
  int i, j, count=0, cur= 0;
  for (j=0; j < bmc->height; j++)
    for (i=0; i < ((bmc->width+7) & (-8)); i++) {
      cur= cur << 1;
      if ((i<bmc->width) && (bmc->get_x(i,j)>0)) cur++;
      count++;
      if (count==4) {
	hex_code << hex_string[cur];
	cur  = 0;
	count= 0;
      }
    }

  int d1= bmc->width;
  int d2= bmc->height;
  int d3= 130+ bmc->xoff;
  int d4= 126+ bmc->yoff;
  int d5= bmc->lwidth;
  if ((d1<256) && (d2<256) && (d3<256) && (d4<256) && (d5<256)) {
    hex_code << as_hexadecimal (d1) << as_hexadecimal (d2)
	     << as_hexadecimal (d3) << as_hexadecimal (d4)
	     << as_hexadecimal (d5);
    hex_code= "<" * hex_code * ">";
  }
  else {
    hex_code= "[<" * hex_code * ">";
    hex_code << as_string (d1) << " " << as_string (d2) << " "
	     << as_string (d3) << " " << as_string (d4) << " "
	     << as_string (d5) << " ";
  }

  tex_chars (char_name)= hex_code;
}

void
printer_rep::generate_tex_fonts () {
  iterator<string> it= iterate (tex_fonts);
  while (it->busy ()) {
    string fn_name= it->next ();
    array<int> a= tex_font_chars [fn_name];
    merge_sort (a);

    int i;
    prologue << "/" << tex_fonts [fn_name]
	     << " " << as_string (N(a))
	     << " " << as_string (a[N(a)-1]+1) << " df\n";
    for (i=0; i<N(a); i++) {
      int end;
      string hex_code= tex_chars [fn_name * "-" * as_string (a[i])];
      for (end=1; end < N(hex_code); end++)
	if (hex_code[end-1]=='>') break;
      string after= hex_code (end, N(hex_code));
      if ((i>0) && (a[i]==(a[i-1]+1))) after << "I";
      else after << as_string (a[i]) << " D";
      if (i==(N(a)-1)) after << " E";
      hex_code= hex_code (0, end);
      
      int j, l, n= N(hex_code);
      for (j=0; j<n; j+=79) {
	if (n < (j+79)) prologue << hex_code (j, n);
	else prologue << hex_code (j, j+79) << "\n";
      }
      l= 79-(n%79);
      if (l<N(after)) prologue << "\n";
      prologue << after << "\n";
    }
  }
}

/******************************************************************************
* graphical routines
******************************************************************************/

color
printer_rep::rgb (int r, int g, int b) {
  return dis->rgb (r, g, b);
}

void
printer_rep::get_rgb (color col, int& r, int& g, int& b) {
  dis->get_rgb (col, r, g, b);
}

color
printer_rep::get_color () {
  return fg;
}

color
printer_rep::get_background () {
  return bg;
}

void
printer_rep::set_color (color c) {
  if (fg==c) return;
  fg= c;
  select_color (c);
}

void
printer_rep::set_background (color c) {
  if (bg==c) return;
  bg= c;
}

void
printer_rep::draw (int c, bitmap_font fn, SI x, SI y) {
  bitmap_char bmc= fn->get(c);
  if (nil (bmc)) return;
  make_tex_char (fn->res_name, c, bmc);
  select_tex_font (fn->res_name);
  move_to (x, y);
  print ("(" * prepare_text (string ((char) c)) * ")p");
  tex_flag= TRUE;
  xpos += bmc->lwidth;
}

void
printer_rep::set_line_style (SI w, int type) {
  (void) type;
  if (lw == w) return;
  lw= w;
  select_line_width (w);
}

void
printer_rep::line (SI x1, SI y1, SI x2, SI y2) {
  print (x1, y1);
  print (x2, y2);
  print (PS_LINE);
}

void
printer_rep::clear (SI x1, SI y1, SI x2, SI y2) {
  select_color (bg);
  print (x1, y1);
  print (x2, y2);
  print (PS_FILL);
  select_color (fg);
}

void
printer_rep::fill (SI x1, SI y1, SI x2, SI y2) {
  if ((x1<x2) && (y1<y2)) {
    print (x1, y1);
    print (x2, y2);
    print (PS_FILL);
  }
}

void
printer_rep::arc (SI x1, SI y1, SI x2, SI y2, int alpha, int delta) {
  print ((x1+x2)/2, (y1+y2)/2);
  print (as_string ((x2-x1)/(2*PIXEL)));
  print (as_string ((y1-y2)/(2*PIXEL)));
  print (as_string (((double) alpha)/64));
  print (as_string (((double) (alpha+delta))/64));
  print (PS_ARC);
}

void
printer_rep::triangle (SI x1, SI y1, SI x2, SI y2, SI x3, SI y3) {
  (void) x1; (void) y1;
  (void) x2; (void) y2;
  (void) x3; (void) y3;
}

void
printer_rep::xpm (string file_name, SI x, SI y) {
  (void) file_name; (void) x; (void) y;
  fatal_error ("Not yet implemented", "printer_rep::xpm");
}

string
incorporate_postscript (string s) {
  int i;
  string r;
  for (i=0; i<N(s); )
    if (s[i]=='%') {
      for (; (i<N(s)) && (s[i]!='\n'); i++);
      if (i<N(s)) i++;
    }
    else {
      for (; (i<N(s)) && (s[i]!='\n'); ) r << s[i++];
      if (i<N(s)) { r << s[i++]; }
    }
  return r;
}

void
printer_rep::postscript (
  string image, string type, SI w, SI h, SI x, SI y,
  int x1, int y1, int x2, int y2)
{
  double sc_x= (72.0/dpi) * ((double) (w/PIXEL)) / ((double) (x2-x1));
  double sc_y= (72.0/dpi) * ((double) (h/PIXEL)) / ((double) (y2-y1));
  cr ();
  cr ();

  print (x, y);
  print ("a");
  print ("currentpoint");
  print ("currentpoint");
  print ("translate");
  print (as_string (sc_x));
  print (as_string (sc_y));
  print ("scale");
  print ("neg");
  print ("exch");
  print ("neg");
  print ("exch");
  print ("translate");
  print (x, y);
  print ("a");
  cr ();
  /* Black Black 248 3155 a currentpoint currentpoint translate
     0.37114 0.37114 scale neg exch neg exch translate 248 3155 a */

  print ("@beginspecial");
  print (as_string (x1));
  print ("@llx");
  print (as_string (y1));
  print ("@lly");
  print (as_string (x2));
  print ("@urx");
  print (as_string (y2));
  print ("@ury");
  print (as_string (10*(x2-x1)));
  print ("@rwi");
  print ("@clip");
  print ("@setspecial");
  cr ();
  /* @beginspecial 0 @llx 0 @lly 613.291260 @urx 613.291260 @ury 6110 @rwi
     @clip @setspecial */
  
  string ps_image= ps_load (image, type);
  body << "%%BeginDocument: " << image << "\n";
  body << incorporate_postscript (ps_image);
  body << "%%EndDocument";
  cr ();

  print ("@endspecial");
  print (x, y);
  print ("a");
  print ("currentpoint");
  print ("currentpoint");
  print ("translate");
  print (as_string (1/sc_x));
  print (as_string (1/sc_y));
  print ("scale");
  print ("neg");
  print ("exch");
  print ("neg");
  print ("exch");
  print ("translate");
  print (x, y);
  print ("a");
  cr ();
  cr ();
  
  /* @endspecial 248 3155 a currentpoint currentpoint translate
     1 0.37114 div 1 0.37114 div scale neg exch neg exch translate
     248 3155 a 660 3073 a ... */

  (void) w; (void) h;
}

bool
printer_rep::check_event (int type) {
  (void) type;
  return FALSE;
}

void
printer_rep::apply_shadow (SI x1, SI y1, SI x2, SI y2) {
  (void) x1; (void) y1; (void) x2; (void) y2;
}

/******************************************************************************
* user interface
******************************************************************************/

ps_device
printer (display dis, string ps_file_name, int dpi, int nr_pages,
	 string page_type, bool landscape, double paper_w, double paper_h)
{
  return new printer_rep (dis, ps_file_name, dpi, nr_pages,
			  page_type, landscape, paper_w, paper_h);
}

#endmodule // code_printer
