/* Library for HP PCL5 language printers (HP LaserJet >=3 and DeskJet series)
   Some commands may not be interpreted by old printers.
   Written by Akos Domotor
   Copyright WFSH and Sapikli TM. */

#include <stdio.h>
#include <stdlib.h>

#undef TEST
const char VERSION_STRING[]="HP_PCL5 v1.1.11  10/05/1998";

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define EXIT_SUCCESS 0
#define EXIT_NOPRINTER -1
#define EXIT_INVALIDPARAM -2
#define HP_SPACING 30         // Spacing between characters, in 1/254ths, compared to point size (height)
#define HP_PAGE_EXECUTIVE 1
#define HP_PAGE_LETTER 2
#define HP_PAGE_LEGAL 3
#define HP_PAGE_A4 26
#define HP_CHARSET_PC8 0x0A55		/* codepage 437 */
#define HP_CHARSET_WIN30LATIN1 0x0955
#define HP_CHARSET_WINLATIN1 0x1355	/* (ANSI) */
#define HP_CHARSET_WINLATIN2 0x0945
#define HP_CHARSET_ISOLATIN1 0x004E	/* ISO 8859-1 */
#define HP_CHARSET_ISOLATIN2 0x024E
#define HP_CHARSET_ISOLATIN5 0x054E
#define HP_CHARSET_SYMBOL 0x134D
#define HP_FONT_COURIER 4099
#define HP_FONT_CGOMEGA 4113
#define HP_FONT_ARIAL 16602
#define HP_FONT_TIMES 16901
#define HP_FONT_SYMBOL 16686
#define HP_FONT_WINDINGS 31402
#define HP_FILL_BLACK 0
#define HP_FILL_WHITE 0x100
#define HP_FILL_GRAY10 0x20A
#define HP_FILL_GRAY20 0x214
#define HP_FILL_GRAY30 0x21E
#define HP_FILL_GRAY40 0x228
#define HP_FILL_GRAY50 0x232
#define HP_FILL_GRAY60 0x23C
#define HP_FILL_GRAY70 0x246
#define HP_FILL_GRAY80 0x250
#define HP_FILL_GRAY90 0x25A
/* Arbitrary gray-level: 0x2nn, where "nn" is the percentage in hexadecimal */
#define HP_FILL_HORIZ 0x301
#define HP_FILL_VERTL 0x302
#define HP_FILL_DIAGL1 0x303
#define HP_FILL_DIAGL2 0x304
#define HP_FILL_SQUARE 0x305
#define HP_FILL_DIAGGRID 0x306


int hp_maxx=4670,hp_maxy=6730;		/* Paper sizes in pixels */
static int aktfont=HP_FONT_COURIER;
static float aktsize=12.0;
static int aktres=600;
static FILE *printer=NULL;

void hp_init_printer(FILE* prn)
{
  printer=prn;
}

void hp_close_printer(void)
{
  printer=NULL;
}

int hp_startjob(int pagesize, int copies, int orientation, int dpi)
/*
   pagesize = [0: default, 1: executive ... 26: A4 (defined in HP_PAGE_*)]
   copies = # of copies (1-99)
   orientation = [0: portrait, 1: landscape, 2: reverse portrait, 3: reverse landscape]
   dpi = printer DPI (300, 600 etc.)
*/
{
  if (printer==NULL) return EXIT_NOPRINTER;

  fprintf(printer,"\033%%-12345X@PJL COMMENT HPPCL5.C driver %s\r\n@PJL COMMENT Copyright WFSH and Sapikli TM.\r\n",VERSION_STRING);
  fprintf(printer,"@PJL SET RESOLUTION=%d\r\n",dpi);
  fputs("@PJL SET PAGEPROTECT=AUTO\r\n",printer);
  fputs("@PJL ENTER LANGUAGE=PCL\r\n\033%A\033E\033&u",printer);
  fprintf(printer,"%dD",dpi);
  fputs("\033%0BINSP1PP1MC1,252WU0PW0UL2,12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.5;",printer);
  fputs("UL5,40,25,10,25;UL6,35,15,10,15,10,15;LT0,8.5,1LTTR0LA1,4,2,1\033%0A\033&l",printer);
  if (copies>0 && copies<100) fprintf(printer,"%dX",copies);
  else fputs("1X",printer);
  fputs("\033&l1H\033&l",printer);
/* ^&l1H = ? */
  if (orientation>=0 && orientation<=3) fprintf(printer,"%do",orientation);
  if (pagesize>0 && pagesize<200) fprintf(printer,"%da",pagesize);
  fputs("4d1e45F",printer);
  switch (pagesize) {
    case HP_PAGE_A4: {
      hp_maxx=4670;
      hp_maxy=6730;
    } break;
    default: {
      hp_maxx=5607;
      hp_maxy=8103;
    }
  }
  fprintf(printer,"\033*r0F\033*p0X\033*p0Y\033*c5607x8103Y\033*c0T\033*t%dR",dpi);
/* ^*c#x#Y = ?  (5607,8103)
   ^*c0T = ?
*/
  fputs("\033%0BIP0,0,1016,1016;SC0,600,0,600;\033%0A",printer);
  fputs("\033(9E\033(s16901t0b0s12.0v1P",printer);
  return EXIT_SUCCESS;
}

int hp_endjob(void)
{
  if (printer==NULL) return EXIT_NOPRINTER;

  fputs("\014\033E\033%-12345X",printer);
  return EXIT_SUCCESS;
}


int hp_movecursor(int x, int y)
/*
  x,y = coordinates in pixels
*/
{
  if (printer==NULL) return EXIT_NOPRINTER;

  if (x<hp_maxx && x>=0) fprintf(printer,"\033*p%dX",x);
  else return EXIT_INVALIDPARAM;
  if (y<hp_maxy && y>=0) fprintf(printer,"\033*p%dY",y);
  else return EXIT_INVALIDPARAM;
  return EXIT_SUCCESS;
}


int hp_settextparams(int direction, int charset, int style, int weight, int underline, float size, int font)
/*
   direction = print direction, degrees of rotation clockwise (90 degree inc. only)
   charset = symbol set [0x0945: Windows Latin 2 ... defined in HP_CHARSET_*]
   style = character style [bit 0: italic, bit 2: condensed, bit 3: compressed,
			    bit 3+bit 4: expanded, bit 5: outline, bit 6: inline,
			    bit 7: shadowed] -> 0: normal, 5: condensed italic
   weight = [-7: ultra thin ... -3: light ... 0: medium (normal) ... 3: bold
	     ... 7: ultra black]
   underline = [negative number: disable, 0: enable fixed, 3: enable floating]
   size = character size in points (1.0 ...)
   font = [16901: Times New Roman, 4099: Courier ... defined in HP_FONT_*]
*/
{
  if (printer==NULL) return EXIT_NOPRINTER;

  switch (direction) {
    case 0:
    case 90:
    case 180:
    case 270: fprintf(printer,"\033&a%dP",direction); break;
    default: return EXIT_INVALIDPARAM;
  }
  if (charset!=0) {
    if (charset%0x100<'A' || charset%0x100>'Z') return EXIT_INVALIDPARAM;
    fprintf(printer,"\033(%d%c",charset>>8,charset%0x100);
  }
  switch (underline) {
    case 0: fputs("\033&d0D",printer); break;
    case 3: fputs("\033&d3D",printer); break;
    default: {
      if (underline>0) return EXIT_INVALIDPARAM;
      else fputs("\033&d@",printer); break;
    }
  }
  fputs("\033(s",printer);
  if (font>0) {
    fprintf(printer,"%dt",font);
    aktfont=font;
  }
  if (weight>=-7 && weight<=7) fprintf(printer,"%db",weight);
  if (style>=0 && style<=255) fprintf(printer,"%ds",style);
  if (size>0) {
    fprintf(printer,"%.2fv",size);
    aktsize=size;
  }
  if (aktfont==HP_FONT_COURIER) fputs("0P",printer);
  else fputs("1P",printer);

  return EXIT_SUCCESS;
}

int hp_drawrectangle(int x, int y, int width, int height, int fill)
/*
  x,y: upper left corner coordinates
  width, height: dimensions in pixels
  fill: [0: solid black, 0x020A: 10% gray ... defined in HP_PATTERN_*]
*/
{
  int i;

  if ((i=hp_movecursor(x,y))<0) return i;
  width=abs(width);
  height=abs(height);
  if (x+width<hp_maxx) fprintf(printer,"\033*c%da",width);
  else return EXIT_INVALIDPARAM;
  if (y+height<hp_maxy) fprintf(printer,"%dB",height);
  else return EXIT_INVALIDPARAM;
  if (fill>>8 <6 && fill>=0) {
    if (fill>>8 == 2 || fill>>8 == 3) fprintf(printer,"\033*c%dG",fill%0x100);
    fprintf(printer,"\033*c%dP",fill>>8);
  }
  else return EXIT_INVALIDPARAM;

  return EXIT_SUCCESS;
}

int hp_setduplex(int mode)
/* mode: duplex mode (0: simplex, 1: duplex, long edge binding,
         2: duplex, short edge binding)
*/
{
  switch (mode) {
    case 1: fputs("\033&l1S",printer); break;
    case 2: fputs("\033&l2S",printer); break;
    case 0: fputs("\033&l1S",printer); break;
  }
  
  return EXIT_SUCCESS;
}


int hp_numwidth(char c)
/*
   Returns the relative width of the character 'c'.
   The width is measured in 1/254 inches.
   These values are basically for Switzerland Bold typeface.
*/
{
  switch (c) {
    case '1': return 108;
    case '2': return 172;
    case '3': return 172;
    case '4': return 178;
    case '5': return 175;
    case '6': return 176;
    case '7': return 177;
    case '8': return 177;
    case '9': return 173;
    case '0': return 175;
    case '-': return 98;
    case '.': return 50;
    default: return 177;
  }
}

int hp_putnumbers(int i, int x, int y, int halign, int valign)
/*
  i: the number to be written
  halign: [0: left, 1: center, 2: right]
  valign: [0: upper edge, 1: center, 2: bottom edge]
*/
{
  int w,f,g,x0,y0,xx;
  char s[8];
  float width;

  sprintf(s,"%d",i);
  if (i==6 || i==9) strcat(s,".");
  for (w=0, f=0; f<strlen(s); f++) w+=hp_numwidth(s[f]);
  w+=(strlen(s)-1)*HP_SPACING;    // Keeping space between characters
  width=aktsize*w/254/100*aktres;    // Width in pixels
  switch (halign) {
    case 1: x0=x-width/2; break;
    case 2: x0=x-width; break;
    default: x0=x;
  }
  switch (valign) {
    case 1: y0=y+aktsize/200*aktres; break;
    case 2: y0=y; break;
    default: y0=y+aktsize/100*aktres;
  }
  if (y0<hp_maxy && y0>=0) fprintf(printer,"\033*p%dY",y0);
  else return EXIT_INVALIDPARAM;
  for (w=0, f=0; f<strlen(s); f++) {
    xx=x0+((float)w/254-(s[f]=='1' ? 0.1:0))*aktres*aktsize/100;
    if (xx<hp_maxx && xx>=0) fprintf(printer,"\033*p%dX",xx);
    else return EXIT_INVALIDPARAM;
    fputc(s[f],printer);
    w+=hp_numwidth(s[f])+HP_SPACING;
  }
  
  return EXIT_SUCCESS;
}


int hp_printtext(int x, int y, char *s)
{
  int f;
  
  if ((f=hp_movecursor(x,y+aktsize*aktres/100))!=EXIT_SUCCESS) return f;
  fprintf(printer,"%s",s);
  return EXIT_SUCCESS;
}


#ifdef TEST
int main(void)
{
  int f;
  FILE *f1;

  f1=fopen("test.pcl","wb");
  hp_init_printer(f1);
  hp_startjob(HP_PAGE_A4,1,0,600);
  hp_settextparams(0,0,0,3,-1,18.0,HP_FONT_TIMES);  /* 18 point Times Bold */
  hp_printtext(500,300,"Test was successful.");
  hp_drawrectangle(1000,1000,500,500,HP_FILL_GRAY50);
  hp_drawrectangle(1000,1500,500,500,HP_FILL_BLACK);
  for (f=0; f<6; f++) hp_drawrectangle(f*1000,200,5,300,HP_FILL_BLACK);
  for (f=0; f<60; f++) hp_drawrectangle(f*100,200,5,100,HP_FILL_BLACK);
  for (f=0; f<9; f++) hp_drawrectangle(200,f*1000,300,5,HP_FILL_BLACK);
  for (f=0; f<90; f++) hp_drawrectangle(200,f*100,100,5,HP_FILL_BLACK);
  
  hp_endjob();
  hp_close_printer();
  fclose(f1);
  return EXIT_SUCCESS;
}
#endif


