// Rotated text drawing with X.

// Original code:
// Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma) */
//
// Modifications for fltk:
// Copyright (c) 1997 Bill Spitzak (spitzak@d2.com)
// Modifications are to draw using the current fl_font.  All fonts
// used are cached in local structures.  This can get real expensive,
// use "

/* xvertext, Copyright (c) 1992 Alan Richardson (mppa3@uk.ac.sussex.syma)
 *
 * 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 appear in all copies and that both the
 * copyright notice and this permission notice appear in supporting
 * documentation.  All work developed as a consequence of the use of
 * this program should duly acknowledge such use. No representations are
 * made about the suitability of this software for any purpose.  It is
 * provided "as is" without express or implied warranty.
 */

// if not defined then portions not used by flwm are included:
#define FLWM 1

/* ********************************************************************** */

#include <FL/x.H>
#include <X11/Xft/Xft.h>
#include <FL/fl_draw.H>
#include "Rotated.H"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

struct BitmapStruct {
  int			 bit_w;
  int			 bit_h;
  Pixmap bm;
};

struct XRotCharStruct {
  int			 ascent;
  int			 descent;
  int			 lbearing;
  int			 rbearing;
  int			 width;
  BitmapStruct	 glyph;
};

struct XRotFontStruct {
  int			 dir;
  int			 height;
  int			 max_ascent;
  int			 max_descent;
  int			 max_char;
  int			 min_char;
  XFontStruct*		 xfontstruct;
  XRotCharStruct	 per_char[256];
};

/* *** Load the rotated version of a given font *** */
 
static XRotFontStruct*
XRotLoadFont(Display *dpy, XFontStruct* fontstruct, int dir)
{
  char val;
  XImage *I1, *I2;
  Pixmap canvas;
  Window root;
  int screen;
  GC font_gc;
  char text[3];/*, errstr[300];*/
  
  XRotFontStruct *rotfont;
  int ichar, i, j, index, boxlen = 60;
  int vert_w, vert_h, vert_len, bit_w, bit_h, bit_len;
  int min_char, max_char;
  unsigned char *vertdata, *bitdata;
  int ascent, descent, lbearing, rbearing;
  int on = 1, off = 0;

  /* useful macros ... */
  screen = DefaultScreen(dpy);
  root = DefaultRootWindow(dpy);

  /* create the depth 1 canvas bitmap ... */
  canvas = XCreatePixmap(dpy, root, boxlen, boxlen, 1);
 
  /* create a GC ... */
  font_gc = XCreateGC(dpy, canvas, 0, 0);
  XSetBackground(dpy, font_gc, off);

  XSetFont(dpy, font_gc, fontstruct->fid);

  /* allocate space for rotated font ... */
  rotfont = (XRotFontStruct *)malloc((unsigned)sizeof(XRotFontStruct));
   
  /* determine which characters are defined in font ... */
  min_char = fontstruct->min_char_or_byte2; 
  if (min_char<0)  min_char = 0;
  rotfont->min_char = min_char;
  max_char = fontstruct->max_char_or_byte2;
  if (max_char>255) max_char = 255;
  rotfont->max_char = max_char;
     
  /* some overall font data ... */
  rotfont->dir = dir;
  rotfont->max_ascent = fontstruct->max_bounds.ascent;
  rotfont->max_descent = fontstruct->max_bounds.descent;   
  rotfont->height = rotfont->max_ascent+rotfont->max_descent;

  rotfont->xfontstruct = fontstruct;
  /* remember xfontstruct for `normal' text ... */
  if (dir != 0) {
    /* font needs rotation ... */
    /* loop through each character ... */
    for (ichar = min_char; ichar <= max_char; ichar++) {

      index = ichar-fontstruct->min_char_or_byte2;
 
      /* per char dimensions ... */
      ascent =   rotfont->per_char[ichar].ascent = 
	fontstruct->per_char[index].ascent;
      descent =  rotfont->per_char[ichar].descent = 
	fontstruct->per_char[index].descent;
      lbearing = rotfont->per_char[ichar].lbearing = 
	fontstruct->per_char[index].lbearing;
      rbearing = rotfont->per_char[ichar].rbearing = 
	fontstruct->per_char[index].rbearing;
      rotfont->per_char[ichar].width = 
	fontstruct->per_char[index].width;

      /* some space chars have zero body, but a bitmap can't have ... */
      if (!ascent && !descent)   
	ascent =   rotfont->per_char[ichar].ascent =   1;
      if (!lbearing && !rbearing) 
	rbearing = rotfont->per_char[ichar].rbearing = 1;

      /* glyph width and height when vertical ... */
      vert_w = rbearing-lbearing;
      vert_h = ascent+descent;

      /* width in bytes ... */
      vert_len = (vert_w-1)/8+1;   
 
      XSetForeground(dpy, font_gc, off);
      XFillRectangle(dpy, canvas, font_gc, 0, 0, boxlen, boxlen);

      /* draw the character centre top right on canvas ... */
      sprintf(text, "%c", ichar);
      XSetForeground(dpy, font_gc, on);
      XDrawImageString(dpy, canvas, font_gc, boxlen/2 - lbearing,
		       boxlen/2 - descent, text, 1);

      /* reserve memory for first XImage ... */
      vertdata = (unsigned char *) malloc((unsigned)(vert_len*vert_h));
  
      /* create the XImage ... */
      I1 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap,
			0, (char *)vertdata, vert_w, vert_h, 8, 0);

//      if (I1 == NULL) ... do something here
  
      I1->byte_order = I1->bitmap_bit_order = MSBFirst;
   
      /* extract character from canvas ... */
      XGetSubImage(dpy, canvas, boxlen/2, boxlen/2-vert_h,
		   vert_w, vert_h, 1, XYPixmap, I1, 0, 0);
      I1->format = XYBitmap; 
 
      /* width, height of rotated character ... */
      if (dir == 2) { 
	bit_w = vert_w;
	bit_h = vert_h; 
      } else {
	bit_w = vert_h;
	bit_h = vert_w; 
      }

      /* width in bytes ... */
      bit_len = (bit_w-1)/8 + 1;

      rotfont->per_char[ichar].glyph.bit_w = bit_w;
      rotfont->per_char[ichar].glyph.bit_h = bit_h;

      /* reserve memory for the rotated image ... */
      bitdata = (unsigned char *)calloc((unsigned)(bit_h*bit_len), 1);

      /* create the image ... */
      I2 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0,
			(char *)bitdata, bit_w, bit_h, 8, 0); 
 
//    if (I2 == NULL) ... error

      I2->byte_order = I2->bitmap_bit_order = MSBFirst;
 
      /* map vertical data to rotated character ... */
      for (j = 0; j < bit_h; j++) {
	for (i = 0; i < bit_w; i++) {
	  /* map bits ... */
	  if (dir == 1)
	    val = vertdata[i*vert_len + (vert_w-j-1)/8] &
	      (128>>((vert_w-j-1)%8));
   
	  else if (dir == 2)
	    val = vertdata[(vert_h-j-1)*vert_len + (vert_w-i-1)/8] &
	      (128>>((vert_w-i-1)%8));
                    
	  else 
	    val = vertdata[(vert_h-i-1)*vert_len + j/8] & 
	      (128>>(j%8));
        
	  if (val) 
	    bitdata[j*bit_len + i/8] = bitdata[j*bit_len + i/8] |
	      (128>>(i%8));
	}
      }
   
      /* create this character's bitmap ... */
      rotfont->per_char[ichar].glyph.bm = 
	XCreatePixmap(dpy, root, bit_w, bit_h, 1);
     
      /* put the image into the bitmap ... */
      XPutImage(dpy, rotfont->per_char[ichar].glyph.bm, 
		font_gc, I2, 0, 0, 0, 0, bit_w, bit_h);
  
      /* free the image and data ... */
      XDestroyImage(I1);
      XDestroyImage(I2);
      /*      free((char *)bitdata);  -- XDestroyImage does this
	      free((char *)vertdata);*/
    }

  }
  
  for (ichar = 0; ichar < min_char; ichar++)
    rotfont->per_char[ichar] = rotfont->per_char['?'];
  for (ichar = max_char+1; ichar < 256; ichar++)
    rotfont->per_char[ichar] = rotfont->per_char['?'];

  /* free pixmap and GC ... */
  XFreePixmap(dpy, canvas);
  XFreeGC(dpy, font_gc);

  return rotfont;
}

/* *** Free the resources associated with a rotated font *** */

static void XRotUnloadFont(Display *dpy, XRotFontStruct *rotfont)
{
  int ichar;

  if (rotfont->dir != 0) {
    /* loop through each character, freeing its pixmap ... */
    for (ichar = rotfont->min_char; ichar <= rotfont->max_char; ichar++)
      XFreePixmap(dpy, rotfont->per_char[ichar].glyph.bm);
  }
  /* rotfont should never be referenced again ... */
  free((char *)rotfont);
}

/* ---------------------------------------------------------------------- */

/* *** A front end to XRotPaintString : mimics XDrawString *** */

static void
XRotDrawString(Display *dpy, XRotFontStruct *rotfont, Drawable drawable,
	       GC gc, int x, int y, const char *str, int len)
{            
  int i, xp, yp, dir, ichar;

  if (str == NULL || len<1) return;

  dir = rotfont->dir;

  /* a horizontal string is easy ... */
  if (dir == 0) {
    XSetFont(dpy, gc, rotfont->xfontstruct->fid);
    XDrawString(dpy, drawable, gc, x, y, str, len);
    return;
  }

  /* vertical or upside down ... */

  XSetFillStyle(dpy, gc, FillStippled);

  /* loop through each character in string ... */
  for (i = 0; i<len; i++) {
    ichar = ((unsigned char*)str)[i];

    /* suitable offset ... */
    if (dir == 1) {
      xp = x-rotfont->per_char[ichar].ascent;
      yp = y-rotfont->per_char[ichar].rbearing; 
    }
    else if (dir == 2) {
      xp = x-rotfont->per_char[ichar].rbearing;
      yp = y-rotfont->per_char[ichar].descent+1; 
    }
    else {
      xp = x-rotfont->per_char[ichar].descent+1;  
      yp = y+rotfont->per_char[ichar].lbearing; 
    }
                   
    /* draw the glyph ... */
    XSetStipple(dpy, gc, rotfont->per_char[ichar].glyph.bm);

    XSetTSOrigin(dpy, gc, xp, yp);

    XFillRectangle(dpy, drawable, gc, xp, yp,
		   rotfont->per_char[ichar].glyph.bit_w,
		   rotfont->per_char[ichar].glyph.bit_h);

    /* advance position ... */
    if (dir == 1)
      y -= rotfont->per_char[ichar].width;
    else if (dir == 2)
      x -= rotfont->per_char[ichar].width;
    else 
      y += rotfont->per_char[ichar].width;
  }
  XSetFillStyle(dpy, gc, FillSolid);
}

#ifndef FLWM
/* *** Return the width of a string *** */

static int XRotTextWidth(XRotFontStruct *rotfont, const char *str, int len)
{
  int i, width = 0, ichar;

  if (str == NULL) return 0;

  if (rotfont->dir == 0)
    width = XTextWidth(rotfont->xfontstruct, str, strlen(str));

  else
    for (i = 0; i<len; i++) {
      width += rotfont->per_char[((unsigned char*)str)[i]].width;
    }

  return width;
}
#endif

/* ---------------------------------------------------------------------- */

// the public functions use the fltk global variables for font & gc:

static XRotFontStruct* font;

void draw_rotated(const char* text, int n, int x, int y, int angle) {
#if XFT_MAJOR < 2
  if (!text || !*text) return;
  /* make angle positive ... */
  if (angle < 0) do angle += 360; while (angle < 0);
  /* get nearest vertical or horizontal direction ... */
  int dir = ((angle+45)/90)%4;

  if (font && font->xfontstruct == fl_xfont && font->dir == dir) {
    ;
  } else {
    if (font) XRotUnloadFont(fl_display, font);
    font = XRotLoadFont(fl_display, fl_xfont, dir);
  }
  XRotDrawString(fl_display, font, fl_window, fl_gc, x, y, text, n);
#endif
}

#ifndef FLWM
void draw_rotated(const char* text, int x, int y, int angle) {
  if (!text || !*text) return;
  draw_rotated(text, strlen(text), x, y, angle);
}
#endif

static void draw_rot90(const char* str, int n, int x, int y) {
  draw_rotated(str, n, y, -x, 90);
}
void draw_rotated90(
  const char* str, 	// the (multi-line) string
  int x, int y, int w, int h, 	// bounding box
  Fl_Align align) {
  if (!str || !*str) return;
  if (w && h && !fl_not_clipped(x, y, w, h)) return;
  if (align & FL_ALIGN_CLIP) fl_clip(x, y, w, h);
  int a1 = align&(-16);
  if (align & FL_ALIGN_LEFT) a1 |= FL_ALIGN_TOP;
  if (align & FL_ALIGN_RIGHT) a1 |= FL_ALIGN_BOTTOM;
  if (align & FL_ALIGN_TOP) a1 |= FL_ALIGN_RIGHT;
  if (align & FL_ALIGN_BOTTOM) a1 |= FL_ALIGN_LEFT;
  fl_draw(str, -(y+h), x, h, w, (Fl_Align)a1, draw_rot90);
  if (align & FL_ALIGN_CLIP) fl_pop_clip();
}

#ifndef FLWM
static void draw_rot180(const char* str, int n, int x, int y) {
  draw_rotated(str, n, -x, -y, 180);
}
void draw_rotated180(
  const char* str, 	// the (multi-line) string
  int x, int y, int w, int h, 	// bounding box
  Fl_Align align) {
  int a1 = align&(-16);
  if (align & FL_ALIGN_LEFT) a1 |= FL_ALIGN_RIGHT;
  if (align & FL_ALIGN_RIGHT) a1 |= FL_ALIGN_LEFT;
  if (align & FL_ALIGN_TOP) a1 |= FL_ALIGN_BOTTOM;
  if (align & FL_ALIGN_BOTTOM) a1 |= FL_ALIGN_TOP;
  fl_draw(str, -(x+w), -(y+h), w, h, (Fl_Align)a1, draw_rot180);
}

static void draw_rot270(const char* str, int n, int x, int y) {
  draw_rotated(str, n, -y, x, 270);
}
void draw_rotated270(
  const char* str, 	// the (multi-line) string
  int x, int y, int w, int h, 	// bounding box
  Fl_Align align) {
  int a1 = align&(-16);
  if (align & FL_ALIGN_LEFT) a1 |= FL_ALIGN_BOTTOM;
  if (align & FL_ALIGN_RIGHT) a1 |= FL_ALIGN_TOP;
  if (align & FL_ALIGN_TOP) a1 |= FL_ALIGN_LEFT;
  if (align & FL_ALIGN_BOTTOM) a1 |= FL_ALIGN_RIGHT;
  fl_draw(str, y, -(x+w), h, w, (Fl_Align)a1, draw_rot270);
}
#endif



syntax highlighted by Code2HTML, v. 0.9.1