/* 
 * Copyright 1994 Chris Smith
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appears in all copies and that
 * both that copyright notice and this permission notice appears in
 * supporting documentation.  I make no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */

/* Canon escape sequence grunge.

   2-up, 4-up, and landscape printing are done with downloaded fonts.
   I've tried three approaches:
    1. Just download the font and print
    2. Rasterize a whole page and send graphics
    3. Download character pairs and print two lines at a time

   #1 is simplest, and the print head moves at full speed.  But the paper
   advances slowly, because the characters are small.

   #2 is slow.  You don't get bidirectional printing, and it eats CPU time.

   #3 is the current implementation.  It does not keep the print head
   moving at full speed, because the volume of data is too large.  But
   it does print bidirectionally.  It is, net, the fastest.

   A hash table is used to map character pairs to single-char codes,
   e.g., the pair "th" might be downloaded as a two-character glyph
   for the character 'X'.  Then all occurrences of "th" are printed
   by sending 'X'.

   There's only room for 100-200 different glyphs, after which we
   flush the font memory in the printer and start over.  Doing this in
   the middle of a line makes the print head shoot over to the margin
   and then back, which increases wear&tear, so we only do font flushes
   at line breaks.  That requires two passes over each line pair, one
   to download any necessary glyphs, one to send the codes for the
   character pairs.

   For documents printed with device fonts none of this nonsense is
   necessary. */


#include "bj.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>

/* Handy abbrevs */

#define put1(c) putchar (c)
#define put2b(w) (put1 ((w) >> 8), put1 (w))
#define put2l(w) (put1 (w), put1 ((w) >> 8))

/* Option bits for ESC I command */

#define DRAFT	0
#define LQ	2

#define CPI10	0
#define CPI12	8
#define CPI17	16
#define CPIPS	1

#define RESIDENT 0
#define DOWNLOAD 4

/* Magic numbers for ESC [ I command */

struct fontid
{
  char *tag;
  int fid[4];
};

static const struct fontid fontid[] =
{
  /*        cpi = 10  12  17   ps */
  { "courier",  { 11, 85, 254, 171 }},
  { "gothic",	{ 36, 87, 255, 174 }},
  { "prestige",	{ 12, 86, 256, 164 }},
};

/* Overstrike replacements in BJ (standard PC) char set */

#define LSQ '\''
#define RSQ '`'

static const uchar overstrike_replacement[][3] =
{
  { 'e', LSQ, 130 },
  { 'a', RSQ, 133 },
  { 'e', RSQ, 138 },
  { 'i', RSQ, 141 },
  { 'E', RSQ, 144 },
  { 'o', RSQ, 149 },
  { 'u', RSQ, 151 },
  { 'a', LSQ, 160 },
  { 'i', RSQ, 161 },
  { 'o', RSQ, 162 },
  { 'u', RSQ, 163 },
  { 'n', '~', 164 },
  { 'N', '~', 165 },
  { '+', '_', 241 },
  { '>', '_', 242 },
  { '<', '_', 243 },
};

/* downloadable fonts */
extern uchar port_17x24[];
extern uchar land_37x22[];
extern uchar land_45x24[];

/* selected downloadable font, if needed */
uchar *myfont;
uchar *ch_bitmap[256];

/* Manual suggests that there is a 32K limit on downloaded bit maps.
   We flush download memory and start over it fills up. */
static int n_downloads_left;

/* Hash table of downloaded character pairs.
   The hash index is the single char code the pair is downloaded into. */

struct hash {
  unsigned char c1, c2;
  unsigned valid:1, reserved:1, downloaded:1, quoted:1;
};

struct hash hash[256];

static void init_hash (void);
static void flush_font (void);
static int hash_pair (uchar c1, uchar c2);
static void send_glyph (uchar c);
static void unpack_myfont (void);

/* Buffer holding a line pair to be printed in one pass */
static uchar line1[MAXCOL], line2[MAXCOL];
static int coln;

/* Bit sieve for making draft glyphs */
unsigned draft_mask;

int check_font_name (const char *name)
{
  int n;

  if (name == 0)
    return 1;

  for (n = 0; n < sizeof fontid / sizeof *fontid; n++)
    if (! strcasecmp (name, fontid[n].tag))
      return 1;

  return 0;
}

void init_font (int lflag, int x2flag, int x4flag)
{
  if (lflag)
    myfont = land_45x24;
  else if (x2flag)
    myfont = land_37x22;
  else if (x4flag)
    myfont = port_17x24;
  else
    myfont = 0;

  if (myfont)
    unpack_myfont ();
}

void do_init (const char *font, int cpi, int code_page, int draft,
	      float lpi, float page_length)
{
  if (myfont)
    init_hash ();

  /* set draft bit mask for font downloads */
  if (draft)
    draft_mask = 0xaaaaaaaa;
  else
    draft_mask = -1;

  /* basic init string, everything off except \n -> crlf */
  fwrite ("\033[K\004\000\001\044\020\000", 9, 1, stdout);

  /* initialize download memory so ESC [ I font selection works. */
  fwrite ("\033=\001\000\045", 5, 1, stdout);

  /* download space so printer will honor request to select download font */
  if (myfont)
    send_glyph (hash_pair (' ', ' '));

  /* set font */

  if (! font) {
    int opts = 0;

    switch (cpi) {
    case 10: opts |= CPI10;
    when 12: opts |= CPI12;
    when 17: opts |= CPI17;
    when -1: opts |= CPIPS; }

    if (draft)
      opts |= DRAFT;
    else
      opts |= LQ;

    /* set the chosen char pitch and draft/LQ mode */
    if (! myfont) {
      put1 ('\033'); put1 ('I'); put1 (opts); }
    else {
      fwrite ("\033I\007\033[I\005\000\000\000\000\000\002\0337",
	      15, 1, stdout); }}
  else {
    int n, ix;

    for (n = 0; n < sizeof fontid / sizeof *fontid; n++)
      if (! strcasecmp (font, fontid[n].tag))
	break;

    switch (cpi) {
    case 10: ix = 0;
    when 12: ix = 1;
    when 17: ix = 2;
    when -1: ix = 3; }

    put1 ('\033'); put1 ('['); put1 ('I');
    put2l (5);
    put2b (fontid[n].fid[ix]);
    put2b (cpi == -1 ? 0 : 1440 / cpi);
    put1 (cpi == -1 ? 2 : 1); }

  /* set code page */

  if (code_page && code_page != 437) {
    put1 ('\033'); put1 ('['); put1 ('T');
    put2l (4);
    put2b (0);
    put2b (code_page); }
    
  /* set baselineskip */

  if (lpi != 6 || myfont) {
    int baselineskip;

    if (myfont)
      baselineskip = myfont[1];
    else
      baselineskip = 0.5 + 360.0 / lpi;

    put1 ('\033'); put1 ('['); put1 ('\\');
    put2l (4);
    put2l (0);
    put2l (360);

    put1 ('\033'); put1 ('3'); put1 (baselineskip);
  }
}

void do_fin ()
{
  const char *fin;
  int len;
  fin_string (&fin, &len);
  fwrite (fin, 1, len, stdout);
}

void fin_string (const char **string, int *len)
{
  /* Reset to dip switch settings.  Reset download memory to resident fonts. */
  static const char fin[] = "\033[K\004\000\001\044\200\200";

  *string = fin;
  *len = sizeof fin - 1;
}

void do_hskip (int pos)
{
  if (pos) {
    put1 ('\033');
    put1 ('d');
    put2l (pos);
  }
}

void send_quoted (uchar c)
{
  put1 ('\033'), put1 ('^');
  put1 (c);
}

void bold_on ()
{
  put1 ('\033'), put1 ('G');
}

void bold_off ()
{
  put1 ('\033'), put1 ('H');
}

void underline_on ()
{
  put1 ('\033'), put1 ('-'), put1 ('1');
}

void underline_off ()
{
  put1 ('\033'), put1 ('-'), put1 ('0');
}

uchar replace_overstrike (uchar a, uchar b)
{
  int n;
  int nov = sizeof overstrike_replacement / sizeof *overstrike_replacement;

  for (n = 0; n < nov; n++)
    if ((overstrike_replacement[n][0] == a
	 && overstrike_replacement[n][1] == b)
	|| (overstrike_replacement[n][0] == b
	    && overstrike_replacement[n][1] == a))
      return overstrike_replacement[n][2];
  return 0;
}

/* All this grief exists so we can produce this: a sequence of font
   download commands that define char-above-char glyphs for the line
   pair, followed by a string of char codes using the defined glyphs.
   It would be much easier just to send each glyph right before using
   it, or to output the data in graphics mode, but neither method
   prints smoothly and bidirectionally. */

void send_pair (uchar c1, uchar c2)
{
  line1[coln] = c1;
  line2[coln] = c2;
  coln++;
}

void send_linebuf (int hskip)
{
  int n, c;
  uchar line[MAXCOL];

  /* see if the already-downloaded glyphs plus any new ones needed for
     this line pair will fit into the printer's memory. */
 again:
  for (n = 0; n < coln; n++) {
    c = hash_pair (line1[n], line2[n]);
    if (c == -1) {
      /* No.  Flush the printer font cache and start again.  The printer
	 memory will hold at least one line, so we will succeed next time. */
      flush_font ();
      goto again; }
    else
      line[n] = c; }

  /* ok, now download the new glyphs needed by this line pair. */

  for (n = 0; n < coln; n++) {
    c = line[n];
    if (! hash[c].downloaded)
      send_glyph (c); }

  /* now transmit the line pair using the two-char glyphs. */

  do_hskip (hskip);
  for (n = 0; n < coln; n++) {
    c = line[n];
    if (hash[c].quoted)
      put1 ('\033'), put1 ('^');
    put1 (c); }

  /* reset coln for next time. */
  coln = 0;
}

static void flush_font ()
{
  init_hash ();
  fwrite ("\033=\001\000\045", 5, 1, stdout);
  send_glyph (hash_pair (' ', ' '));
}

static void init_hash ()
{
  uchar c;

  memset (hash, 0, sizeof hash);

  for (c = 0; c < 040; c++)
    hash[c].quoted = 1;
  for (c = 0177; c < 0240; c++)
    hash[c].quoted = 1;

  hash['\n'].reserved = hash['\n'].valid = 1;
  hash['\f'].reserved = hash['\f'].valid = 1;
  hash['\033'].reserved = hash['\033'].valid = 1;

  /* each download takes 6 bytes per column, 6 * width bytes total. */
  n_downloads_left = 32768 / (6 * myfont[0]);

  /* it also takes one of the 253 char slots */
  if (n_downloads_left > 252)
    n_downloads_left = 252;
}

/* hash the character pair (c1,c2) and return the char code to use for it.
   if there is no space for a new code, return -1. */

static int hash_pair (uchar c1, uchar c2)
{
  unsigned h = (c1 * 0x9e9f) ^ (c2 * 0x0a01);
  unsigned ix = (h >> 8) & 255;
  unsigned dix;

  if (hash[ix].valid) {
    if (hash[ix].c1 == c1 && hash[ix].c2 == c2 && ! hash[ix].reserved)
      return ix;
    dix = h | 1;

    for (;;) {
      ix = (ix + dix) & 255;
      if (hash[ix].c1 == c1 && hash[ix].c2 == c2 && ! hash[ix].reserved)
	return ix;
      if (! hash[ix].valid)
	break;
    }
  }

  if (n_downloads_left == 0)
    return -1;
  n_downloads_left--;

  hash[ix].c1 = c1; hash[ix].c2 = c2; hash[ix].valid = 1;
  return ix;
}

static void send_glyph (uchar c)
{
  uchar *ch1 = ch_bitmap[hash[c].c1];
  uchar *ch2 = ch_bitmap[hash[c].c2];
  int pxlw = myfont[0];
  unsigned fourbytes, mask;
  int x, nsh;

  /* mark downloaded */
  hash[c].downloaded = 1;

  /* download command */
  put1 ('\033'); put1 ('=');

  /* length of following data */
  put2l (11 + 6 * pxlw);

  /* printer id: bj 200 */
  put1 (37);

  /* code page, 437 */
  put2b (437);

  /* font id 0 */
  put2b (0);

  /* char set 1, proportional */
  put1 (0x10);

  /* gratuitious null */
  put1 (0);

  /* char code */
  put1 (c);

  /* char edit attributes (line drawing, etc): none */
  put1 (0);

  /* char width, dots */
  put1 (pxlw);

  /* gratuitous null */
  put1 (0);

  /* get mask for sieving glyph for draft mode */
  mask = draft_mask;

  /* [must write more code to handle case where cell width isn't 3 bytes] */
  assert (16 < myfont[1] && myfont[1] <= 24);
  nsh = 24 - myfont[1];

  /* send pxlw columns, 6 bytes each, msb first */
  for (x = 0; x < pxlw; x++) {
    /* first two bytes come from top char */
    put1 (ch1[0] & mask);
    put1 (ch1[1] & mask);

    /* the last four include the bottom char, shifted up nsh */ 
    fourbytes = (ch1[2] << 24
		 | ch2[0] << (nsh + 16)
		 | ch2[1] << (nsh + 8)
		 | ch2[2] << nsh);
    fourbytes &= mask;
    put1 (fourbytes >> 24);
    put1 (fourbytes >> 16);
    put1 (fourbytes >> 8);
    put1 (fourbytes >> 0);

    if (mask != -1)
      mask = ~mask;

    ch1 += 3;
    ch2 += 3;
  }
}

static void unpack_myfont ()
{
  int c, pxlw, pxlh, bytes_per_bitmap;
  uchar *ch, *ch_space;

  bzero (ch_bitmap, sizeof ch_bitmap);

  /* char cell dimensions are stored in the first two bytes.  this is
     followed by a sequence of one or more char specs and then a zero
     byte.  a char spec is a char code, one byte, followed by the bit
     map for the char.  the bitmaps are stored by columns, leftmost
     first, pxlh bits per column, pxlw columns.  each column starts on
     a byte boundary. */

  pxlw = myfont[0];
  pxlh = myfont[1];
  bytes_per_bitmap = ((pxlh + 7) >> 3) * pxlw;

  /* store a pointer to the bitmap for each char c in ch_bitmap[c] */

  ch = myfont + 2;
  do {
    c = ch[0];
    if (c == ' ')
      ch_space = ch;
    ch_bitmap[c] = ch = ch + 1;
    ch += bytes_per_bitmap;
  } while (*ch != 0);

  /* point undefined chars to space, so there will be some bitmap
     for every char code. */
  for (c = 0; c < 256; c++)
    if (! ch_bitmap[c])
      ch_bitmap[c] = ch_space;
}
