Logo Search packages:      
Sourcecode: r-base-core-ra version File versions  Download package

rotated.c

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

/* xvertext 5.0, Copyright (c) 1993 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.
 */

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


/* BETTER: xvertext now does rotation at any angle!!
 *
 * BEWARE: function arguments have CHANGED since version 2.0!!
 *
 * Protoized (ANSI C, no longer old K&R C):  Martin Maechler, R core team.
 */

/* The version for R 2.1.0 is based on patches by
   Ei-ji Nakama <nakama@ki.rim.or.jp> for use in Japanese. */

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

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#if !defined(strdup) && defined(HAVE_DECL_STRDUP) && !HAVE_DECL_STRDUP
extern char *strdup(const char *s1);
#endif

#ifdef SUPPORT_MBCS
# define USE_FONTSET 1
extern int utf8locale;
/* In theory we should do this, but it works less well
# ifdef X_HAVE_UTF8_STRING
#  define HAVE_XUTF8TEXTESCAPEMENT 1
#  define HAVE_XUTF8TEXTEXTENTS 1
#  define HAVE_XUTF8DRAWSTRING 1
# endif */
#endif

#include "rotated.h"

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

/* text alignment -- only NONE is used in R */

#define NONE             0
#define TLEFT            1
#define TCENTRE          2
#define TRIGHT           3
#define MLEFT            4
#define MCENTRE          5
#define MRIGHT           6
#define BLEFT            7
#define BCENTRE          8
#define BRIGHT           9

/* Make sure cache size is set */

#ifndef CACHE_SIZE_LIMIT
#define CACHE_SIZE_LIMIT 0
#endif /*CACHE_SIZE_LIMIT */

/* Make sure a cache method is specified */

#ifndef CACHE_XIMAGES
#ifndef CACHE_BITMAPS
#define CACHE_BITMAPS
#endif /*CACHE_BITMAPS*/
#endif /*CACHE_XIMAGES*/

#ifndef DEG2RAD
#define DEG2RAD 0.01745329251994329576
#endif

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


/* Debugging macros */

#ifdef DEBUG
static int debug=1;
#else
static int debug=0;
#endif /*DEBUG*/

#define DEBUG_PRINT1(a) if (debug) printf (a)
#define DEBUG_PRINT2(a, b) if (debug) printf (a, b)
#define DEBUG_PRINT3(a, b, c) if (debug) printf (a, b, c)
#define DEBUG_PRINT4(a, b, c, d) if (debug) printf (a, b, c, d)
#define DEBUG_PRINT5(a, b, c, d, e) if (debug) printf (a, b, c, d, e)


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

static double myround(double x)
{
    return floor(x+0.5);
}


/* A structure holding everything needed for a rotated string */

00119 typedef struct rotated_text_item_template {
    Pixmap bitmap;
    XImage *ximage;

    char *text;
    char *font_name;
    Font fid;
    double angle;
    int align;
    double magnify;

    int cols_in;
    int rows_in;
    int cols_out;
    int rows_out;

    int nl;
    int max_width;
    double *corners_x;
    double *corners_y;

    long int size;
    int cached;

    struct rotated_text_item_template *next;
} RotatedTextItem;

static RotatedTextItem *first_text_item=NULL;


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


/* A structure holding current magnification and bounding box padding */

00154 static struct style_template {
    double magnify;
    int bbx_pad;
} style={
    1.,
    0
    };


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


double                   XRotVersion(char *str, int n);
void              XRotSetMagnification(double m);
void              XRotSetBoundingBoxPad(int p);
int               XRotDrawString(Display *dpy, XFontStruct *font, double angle, Drawable drawable, GC gc, int x, int y, const char *str);
int               XRotDrawImageString(Display *dpy, XFontStruct *font, double angle, Drawable drawable, GC gc, int x, int y, const char *str);
int               XRotDrawAlignedString(Display *dpy, XFontStruct *font, double angle, Drawable drawable, GC gc, int x, int y, const char *text, int align);
int               XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, double angle, Drawable drawable, GC gc, int x, int y, const char *text, int align);
XPoint                   *XRotTextExtents(Display *dpy, XFontStruct *font, double angle, int x, int y, const char *text, int align);

static XImage            *MakeXImage(Display *dpy, int w, int h);
static int        XRotPaintAlignedString(Display *dpy, XFontStruct *font, double angle, Drawable drawable, GC gc, int x, int y, const char *text, int align, int bg);
static int        XRotDrawHorizontalString(Display *dpy, XFontStruct *font, Drawable drawable, GC gc, int x, int y, const char *text, int align, int bg);
static RotatedTextItem *XRotRetrieveFromCache(Display *dpy, XFontStruct *font, double angle, const char *text, int align);
static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font, double angle, const char *text, int align);
static void       XRotAddToLinkedList(Display *dpy, RotatedTextItem *item);
static void       XRotFreeTextItem(Display *dpy, RotatedTextItem *item);
static XImage            *XRotMagnifyImage(Display *dpy, XImage *ximage);

#ifdef USE_FONTSET
static int XmbRotDrawString(Display *dpy, XFontSet fontset, double angle,
                      Drawable drawable, GC gc, int x, int y, const char *str);
#endif

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

int XRfRotDrawString(Display *dpy, R_XFont *rfont, double angle,
                 Drawable drawable, GC gc, int x, int y, const char *str)
{
#ifdef USE_FONTSET
    if(rfont->type == Font_Set)
      return XmbRotDrawString(dpy, rfont->fontset, angle, drawable, gc, x, y, str);
    else
#endif
      return XRotDrawString(dpy, rfont->font, angle, drawable, gc, x, y, str);
}




/**************************************************************************/
/* Return version/copyright information                             */
/**************************************************************************/

double XRotVersion(char *str, int n)
{
    if(str!=NULL)
      strncpy(str, XV_COPYRIGHT, n);
    return XV_VERSION;
}


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


/**************************************************************************/
/* Set the font magnification factor for all subsequent operations        */
/**************************************************************************/

void XRotSetMagnification(double m)
{
    if(m>0.)
      style.magnify=m;
}


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


/**************************************************************************/
/* Set the padding used when calculating bounding boxes                   */
/**************************************************************************/

void XRotSetBoundingBoxPad(int p)
{
    if(p>=0)
      style.bbx_pad=p;
}


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


/**************************************************************************/
/*  Create an XImage structure and allocate memory for it           */
/**************************************************************************/

static XImage *MakeXImage(Display *dpy, int w, int h)
{
    XImage *I;
    char *data;

    /* reserve memory for image */
    data=(char *)calloc((unsigned)(((w-1)/8+1)*h), 1);
    if(data==NULL)
      return NULL;

    /* create the XImage */
    I=XCreateImage(dpy, DefaultVisual(dpy, DefaultScreen(dpy)), 1, XYBitmap,
               0, data, w, h, 8, 0);
    if(I==NULL)
      return NULL;

    I->byte_order=I->bitmap_bit_order=MSBFirst;
    return I;
}


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


/**************************************************************************/
/*  A front end to XRotPaintAlignedString:                          */
/*    -no alignment, no background                            */
/**************************************************************************/

int XRotDrawString(Display *dpy, XFontStruct *font, double angle,
               Drawable drawable, GC gc, int x, int y, const char *str)
{
    return (XRotPaintAlignedString(dpy, font, angle, drawable, gc,
                           x, y, str, NONE, 0));
}


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


/**************************************************************************/
/*  A front end to XRotPaintAlignedString:                          */
/*    -no alignment, paints background                        */
/**************************************************************************/

int XRotDrawImageString(Display *dpy, XFontStruct *font, double angle,
                  Drawable drawable, GC gc, int x, int y, const char *str)
{
    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
                          x, y, str, NONE, 1));
}


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


/**************************************************************************/
/*  A front end to XRotPaintAlignedString:                          */
/*    -does alignment, no background                                */
/**************************************************************************/

int XRotDrawAlignedString(Display *dpy, XFontStruct *font, double angle,
                    Drawable drawable, GC gc, int x, int y,
                    const char *text, int align)
{
    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
                          x, y, text, align, 0));
}


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


/**************************************************************************/
/*  A front end to XRotPaintAlignedString:                          */
/*    -does alignment, paints background                      */
/**************************************************************************/

int XRotDrawAlignedImageString(Display *dpy, XFontStruct *font, double angle,
                         Drawable drawable, GC gc, int x, int y,
                         const char *text, int align)
{
    return(XRotPaintAlignedString(dpy, font, angle, drawable, gc,
                          x, y, text, align, 1));
}


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


/**************************************************************************/
/*  Aligns and paints a rotated string                              */
/**************************************************************************/

static int XRotPaintAlignedString(Display *dpy, XFontStruct *font, double angle,
                          Drawable drawable, GC gc, int x, int y,
                          const char *text, int align, int bg)
{
    int i;
    GC my_gc;
    int xp, yp;
    double hot_x, hot_y;
    double hot_xp, hot_yp;
    double sin_angle, cos_angle;
    RotatedTextItem *item;
    Pixmap bitmap_to_paint;

    /* return early for NULL/empty strings */
    if(text==NULL || *text=='\0')
      return 0;

    if(strlen(text)==0)
      return 0;

    /* manipulate angle to 0<=angle<360 degrees */
    while(angle<0)
      angle+=360;

    while(angle>=360)
      angle-=360;

    angle *= DEG2RAD;

    /* horizontal text made easy */
    if(angle==0. && style.magnify==1.)
      return(XRotDrawHorizontalString(dpy, font, drawable, gc, x, y,
                              text, align, bg));

    /* get a rotated bitmap */
    item=XRotRetrieveFromCache(dpy, font, angle, text, align);
    if(item==NULL)
      return 0;

    /* this gc has similar properties to the user's gc */
    my_gc=XCreateGC(dpy, drawable, (unsigned long)0, 0);
    XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask
          |GCClipMask,
          my_gc);

    /* alignment : which point (hot_x, hot_y) relative to bitmap centre
       coincides with user's specified point? */

    /* y position */
    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
      hot_y=(double)item->rows_in/2*style.magnify;
    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
      hot_y=0;
    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
      hot_y= -(double)item->rows_in/2*style.magnify;
    else
      hot_y= -((double)item->rows_in/2-(double)font->descent)*style.magnify;

    /* x position */
    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
      hot_x= -(double)item->max_width/2*style.magnify;
    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
      hot_x=0;
    else
      hot_x=(double)item->max_width/2*style.magnify;

    /* pre-calculate sin and cos */
    sin_angle = myround(sin(angle)*1000.0) / 1000.0;
    cos_angle = myround(cos(angle)*1000.0) / 1000.0;

    /* rotate hot_x and hot_y around bitmap centre */
    hot_xp = hot_x*cos_angle - hot_y*sin_angle;
    hot_yp = hot_x*sin_angle + hot_y*cos_angle;

    /* text background will be drawn using XFillPolygon */
    if(bg) {
      GC depth_one_gc;
      XPoint *xpoints;
      Pixmap empty_stipple;

      /* reserve space for XPoints */
      xpoints=(XPoint *)malloc((unsigned)(4*item->nl*sizeof(XPoint)));
      if(!xpoints)
          return 1;

      /* rotate corner positions */
      for(i=0; i<4*item->nl; i++) {
          xpoints[i].x=(double)x + ( (item->corners_x[i]-hot_x)*cos_angle +
                              (item->corners_y[i]+hot_y)*sin_angle);
          xpoints[i].y=(double)y + (-(item->corners_x[i]-hot_x)*sin_angle +
                              (item->corners_y[i]+hot_y)*cos_angle);
      }

      /* we want to swap foreground and background colors here;
         XGetGCValues() is only available in R4+ */

      empty_stipple=XCreatePixmap(dpy, drawable, 1, 1, 1);

      depth_one_gc=XCreateGC(dpy, empty_stipple, (unsigned long)0, 0);
      XSetForeground(dpy, depth_one_gc, 0);
      XFillRectangle(dpy, empty_stipple, depth_one_gc, 0, 0, 2, 2);

      XSetStipple(dpy, my_gc, empty_stipple);
      XSetFillStyle(dpy, my_gc, FillOpaqueStippled);

      XFillPolygon(dpy, drawable, my_gc, xpoints, 4*item->nl, Nonconvex,
                 CoordModeOrigin);

      /* free our resources */
      free((char *)xpoints);
      XFreeGC(dpy, depth_one_gc);
      XFreePixmap(dpy, empty_stipple);
    }

    /* where should top left corner of bitmap go ? */
    xp=(double)x-((double)item->cols_out/2 +hot_xp);
    yp=(double)y-((double)item->rows_out/2 -hot_yp);

    /* by default we draw the rotated bitmap, solid */
    bitmap_to_paint=item->bitmap;

    /* handle user stippling */
#ifndef X11R3
    {
      GC depth_one_gc;
      XGCValues values;
      Pixmap new_bitmap, inverse;

      /* try and get some GC properties */
      if(XGetGCValues(dpy, gc,
                  GCStipple|GCFillStyle|GCForeground|GCBackground|
                  GCTileStipXOrigin|GCTileStipYOrigin,
                  &values)) {

          /* only do this if stippling requested */
          if((values.fill_style==FillStippled ||
            values.fill_style==FillOpaqueStippled) && !bg) {

            /* opaque stipple: draw rotated text in background colour */
            if(values.fill_style==FillOpaqueStippled) {
                XSetForeground(dpy, my_gc, values.background);
                XSetFillStyle(dpy, my_gc, FillStippled);
                XSetStipple(dpy, my_gc, item->bitmap);
                XSetTSOrigin(dpy, my_gc, xp, yp);
                XFillRectangle(dpy, drawable, my_gc, xp, yp,
                           item->cols_out, item->rows_out);
                XSetForeground(dpy, my_gc, values.foreground);
            }

            /* this will merge the rotated text and the user's stipple */
            new_bitmap=XCreatePixmap(dpy, drawable,
                               item->cols_out, item->rows_out, 1);

            /* create a GC */
            depth_one_gc=XCreateGC(dpy, new_bitmap, (unsigned long)0, 0);
            XSetForeground(dpy, depth_one_gc, 1);
            XSetBackground(dpy, depth_one_gc, 0);

            /* set the relative stipple origin */
            XSetTSOrigin(dpy, depth_one_gc,
                       values.ts_x_origin-xp, values.ts_y_origin-yp);

            /* fill the whole bitmap with the user's stipple */
            XSetStipple(dpy, depth_one_gc, values.stipple);
            XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled);
            XFillRectangle(dpy, new_bitmap, depth_one_gc,
                         0, 0, item->cols_out, item->rows_out);

            /* set stipple origin back to normal */
            XSetTSOrigin(dpy, depth_one_gc, 0, 0);

            /* this will contain an inverse copy of the rotated text */
            inverse=XCreatePixmap(dpy, drawable,
                              item->cols_out, item->rows_out, 1);

            /* invert text */
            XSetFillStyle(dpy, depth_one_gc, FillSolid);
            XSetFunction(dpy, depth_one_gc, GXcopyInverted);
            XCopyArea(dpy, item->bitmap, inverse, depth_one_gc,
                    0, 0, item->cols_out, item->rows_out, 0, 0);

            /* now delete user's stipple everywhere EXCEPT on text */
            XSetForeground(dpy, depth_one_gc, 0);
            XSetBackground(dpy, depth_one_gc, 1);
            XSetStipple(dpy, depth_one_gc, inverse);
            XSetFillStyle(dpy, depth_one_gc, FillStippled);
            XSetFunction(dpy, depth_one_gc, GXcopy);
            XFillRectangle(dpy, new_bitmap, depth_one_gc,
                         0, 0, item->cols_out, item->rows_out);

            /* free resources */
            XFreePixmap(dpy, inverse);
            XFreeGC(dpy, depth_one_gc);

            /* this is the new bitmap */
            bitmap_to_paint=new_bitmap;
          }
      }
    }
#endif /*X11R3*/

    /* paint text using stipple technique */
    XSetFillStyle(dpy, my_gc, FillStippled);
    XSetStipple(dpy, my_gc, bitmap_to_paint);
    XSetTSOrigin(dpy, my_gc, xp, yp);
    XFillRectangle(dpy, drawable, my_gc, xp, yp,
               item->cols_out, item->rows_out);

    /* free our resources */
    XFreeGC(dpy, my_gc);

    /* stippled bitmap no longer needed */
    if(bitmap_to_paint!=item->bitmap)
      XFreePixmap(dpy, bitmap_to_paint);

#ifdef CACHE_XIMAGES
    XFreePixmap(dpy, item->bitmap);
#endif /*CACHE_XIMAGES*/

    /* if item isn't cached, destroy it completely */
    if(!item->cached)
      XRotFreeTextItem(dpy,item);

    /* we got to the end OK! */
    return 0;
}


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


/**************************************************************************/
/*  Draw a horizontal string in a quick fashion                     */
/**************************************************************************/

static int XRotDrawHorizontalString(Display *dpy, XFontStruct *font,
                            Drawable drawable, GC gc, int x, int y,
                            const char *text, int align, int bg)
{
    GC my_gc;
    int nl=1, i;
    int height;
    int xp, yp;
    char *str1, *str2, *str3;
    char *str2_a="\0", *str2_b="\n\0";
    int dir, asc, desc;
    XCharStruct overall;

    if (text == NULL || *text=='\0') {
      DEBUG_PRINT1("Empty string, ignoring\n");
      return 0;
    }

    /* this gc has similar properties to the user's gc (including stipple) */
    my_gc=XCreateGC(dpy, drawable, (unsigned long)0, 0);
    XCopyGC(dpy, gc,
          GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle|
          GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask|GCClipMask, my_gc);
    XSetFont(dpy, my_gc, font->fid);

    /* count number of sections in string */
    if(align!=NONE)
      for(i=strlen(text)-2; i >= 0; i--)
          if(text[i]=='\n')
            nl++;

    /* ignore newline characters if not doing alignment */
    if(align==NONE)
      str2=str2_a;
    else
      str2=str2_b;

    /* overall font height */
    height=font->ascent+font->descent;

    /* y position */
    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
      yp=y+font->ascent;
    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
      yp=y-nl*height/2+font->ascent;
    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
      yp=y-nl*height+font->ascent;
    else
      yp=y;

    str1=strdup(text);
    if(str1==NULL)
      return 1;

    str3=strtok(str1, str2);

    /* loop through each section in the string */
    do {
      XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
                 &overall);

      /* where to draw section in x ? */
      if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
          xp=x;
      else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
          xp=x-overall.rbearing/2;
      else
          xp=x-overall.rbearing;

      /* draw string onto bitmap */
      if(!bg)
          XDrawString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));
      else
          XDrawImageString(dpy, drawable, my_gc, xp, yp, str3, strlen(str3));

      /* move to next line */
      yp+=height;

      str3=strtok((char *)NULL, str2);
    }
    while(str3!=NULL);

    free(str1);
    XFreeGC(dpy, my_gc);

    return 0;
}


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


/**************************************************************************/
/*   Query cache for a match with this font/text/angle/alignment    */
/*     request, otherwise arrange for its creation                  */
/**************************************************************************/

static RotatedTextItem *XRotRetrieveFromCache(Display *dpy, XFontStruct *font,
                                    double angle, const char *text,
                                    int align)
{
    Font fid;
    char *font_name=NULL;
    unsigned long name_value;
    RotatedTextItem *item=NULL;
    RotatedTextItem *i1=first_text_item;

    /* get font name, if it exists */
    if(XGetFontProperty(font, XA_FONT, &name_value)) {
      DEBUG_PRINT1("got font name OK\n");
      font_name=XGetAtomName(dpy, name_value);
      fid=0;
    }
#ifdef CACHE_FID
    /* otherwise rely (unreliably?) on font ID */
    else {
      DEBUG_PRINT1("can't get fontname, caching FID\n");
      font_name=NULL;
      fid=font->fid;
    }
#else
    /* not allowed to cache font ID's */
    else {
      DEBUG_PRINT1("can't get fontname, can't cache\n");
      font_name=NULL;
      fid=0;
    }
#endif /*CACHE_FID*/

    /* look for a match in cache */

    /* matching formula:
       identical text;
       identical fontname (if defined, font ID's if not);
       angles close enough (<0.0001 here, could be smaller);
       HORIZONTAL alignment matches, OR it's a one line string;
       magnifications the same */

    while(i1 && !item) {
      /* match everything EXCEPT fontname/ID */
      if(strcmp(text, i1->text)==0 &&
         fabs(angle-i1->angle)<0.0001 &&
         style.magnify==i1->magnify &&
         (i1->nl==1 ||
          ((align==0)?9:(align-1))%3==
            ((i1->align==0)?9:(i1->align-1))%3)) {

          /* now match fontname/ID */
          if(font_name!=NULL && i1->font_name!=NULL) {
            if(strcmp(font_name, i1->font_name)==0) {
                item=i1;
                DEBUG_PRINT1("Matched against font names\n");
            }
            else
                i1=i1->next;
          }
#ifdef CACHE_FID
          else if(font_name==NULL && i1->font_name==NULL) {
            if(fid==i1->fid) {
                item=i1;
                DEBUG_PRINT1("Matched against FID's\n");
            }
            else
                i1=i1->next;
          }
#endif /*CACHE_FID*/
          else
            i1=i1->next;
      }
      else
          i1=i1->next;
    }

    if(item)
      DEBUG_PRINT1("**Found target in cache.\n");
    if(!item)
      DEBUG_PRINT1("**No match in cache.\n");

    /* no match */
    if(!item) {
      /* create new item */
      item=XRotCreateTextItem(dpy, font, angle, text, align);
      if(!item)
          return NULL;

      /* record what it shows */
      item->text=strdup(text);

      /* fontname or ID */
      if(font_name!=NULL) {
          item->font_name=strdup(font_name);
          item->fid=0;
      }
      else {
          item->font_name=NULL;
          item->fid=fid;
      }

      item->angle=angle;
      item->align=align;
      item->magnify=style.magnify;

      /* cache it */
      XRotAddToLinkedList(dpy, item);
    }

    if(font_name)
      XFree(font_name);

    /* if XImage is cached, need to recreate the bitmap */

#ifdef CACHE_XIMAGES
    {
      GC depth_one_gc;

      /* create bitmap to hold rotated text */
      item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
                           item->cols_out, item->rows_out, 1);

      /* depth one gc */
      depth_one_gc=XCreateGC(dpy, item->bitmap, (unsigned long)0, 0);
      XSetBackground(dpy, depth_one_gc, 0);
      XSetForeground(dpy, depth_one_gc, 1);

      /* make the text bitmap from XImage */
      XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0,
              item->cols_out, item->rows_out);

      XFreeGC(dpy, depth_one_gc);
    }
#endif /*CACHE_XIMAGES*/

    return item;
}


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


/**************************************************************************/
/*  Create a rotated text item                                      */
/**************************************************************************/

static RotatedTextItem *XRotCreateTextItem(Display *dpy, XFontStruct *font,
                                 double angle, const char *text, int align)
{
    RotatedTextItem *item=NULL;
    Pixmap canvas;
    GC font_gc;
    XImage *I_in;
    register int i, j;
    char *str1, *str2, *str3;
    char *str2_a="\0", *str2_b="\n\0";
    int height;
    int byte_w_in, byte_w_out;
    int xp, yp;
    double sin_angle, cos_angle;
    int it, jt;
    double itd, jtd;
    double di, dj;
    int ic=0;
    double xl, xr, xinc;
    int byte_out;
    int dir, asc, desc;
    XCharStruct overall;
    int old_cols_in=0, old_rows_in=0;

    /* allocate memory */
    item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem));
    if(!item)
      return NULL;

    /* count number of sections in string */
    item->nl=1;
    if(align!=NONE)
      for(i=strlen(text)-2; i >= 0; i--)
          if(text[i]=='\n')
            item->nl++;

    /* ignore newline characters if not doing alignment */
    if(align==NONE)
      str2=str2_a;
    else
      str2=str2_b;

    /* find width of longest section */
    str1=strdup(text);
    if(str1==NULL)
      return NULL;

    str3=strtok(str1, str2);

    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
             &overall);

    item->max_width=overall.rbearing;

    /* loop through each section */
    do {
      str3=strtok((char *)NULL, str2);

      if(str3!=NULL) {
          XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
                   &overall);

          if(overall.rbearing>item->max_width)
            item->max_width=overall.rbearing;
      }
    }
    while(str3!=NULL);

    free(str1);

    /* overall font height */
    height=font->ascent+font->descent;

    /* dimensions horizontal text will have */
    item->cols_in=item->max_width;
    item->rows_in=item->nl*height;

    /* fudge in case one of the above is zero: */
    if (!item->cols_in) item->cols_in=1;
    if (!item->rows_in) item->rows_in=1;

    /* bitmap for drawing on */
    canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy),
                   item->cols_in, item->rows_in, 1);

    /* create a GC for the bitmap */
    font_gc=XCreateGC(dpy, canvas, (unsigned long)0, 0);
    XSetBackground(dpy, font_gc, 0);
    XSetFont(dpy, font_gc, font->fid);

    /* make sure the bitmap is blank */
    XSetForeground(dpy, font_gc, 0);
    XFillRectangle(dpy, canvas, font_gc, 0, 0,
               item->cols_in+1, item->rows_in+1);
    XSetForeground(dpy, font_gc, 1);

    /* pre-calculate sin and cos */
    sin_angle = myround(sin(angle)*1000.0) / 1000.0;
    cos_angle = myround(cos(angle)*1000.0) / 1000.0;

    /* text background will be drawn using XFillPolygon */
    item->corners_x=
      (double *)malloc((unsigned)(4*item->nl*sizeof(double)));
    if(!item->corners_x)
      return NULL;

    item->corners_y=
      (double *)malloc((unsigned)(4*item->nl*sizeof(double)));
    if(!item->corners_y)
      return NULL;

    /* draw text horizontally */

    /* start at top of bitmap */
    yp=font->ascent;

    str1=strdup(text);
    if(str1==NULL)
      return NULL;

    str3=strtok(str1, str2);

    /* loop through each section in the string */
    do {
      XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
                 &overall);

      /* where to draw section in x ? */
      if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
          xp=0;
      else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
          xp=(item->max_width-overall.rbearing)/2;
      else
          xp=item->max_width-overall.rbearing;

      /* draw string onto bitmap */
      XDrawString(dpy, canvas, font_gc, xp, yp, str3, strlen(str3));

      /* keep a note of corner positions of this string */
      item->corners_x[ic]=((double)xp-(double)item->cols_in/2)*style.magnify;
      item->corners_y[ic]=((double)(yp-font->ascent)-(double)item->rows_in/2)
          *style.magnify;
      item->corners_x[ic+1]=item->corners_x[ic];
      item->corners_y[ic+1]=item->corners_y[ic]+(double)height*style.magnify;
      item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+
          (double)overall.rbearing*style.magnify;
      item->corners_y[item->nl*4-1-ic]=item->corners_y[ic];
      item->corners_x[item->nl*4-2-ic]=
          item->corners_x[item->nl*4-1-ic];
      item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1];

      ic+=2;

      /* move to next line */
      yp+=height;

      str3=strtok((char *)NULL, str2);
    }
    while(str3!=NULL);

    free(str1);

    /* create image to hold horizontal text */
    I_in=MakeXImage(dpy, item->cols_in, item->rows_in);
    if(I_in==NULL)
      return NULL;

    /* extract horizontal text */
    XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in,
             1, XYPixmap, I_in, 0, 0);
    I_in->format=XYBitmap;

    /* magnify horizontal text */
    if(style.magnify!=1.) {
      I_in=XRotMagnifyImage(dpy, I_in);

      old_cols_in=item->cols_in;
      old_rows_in=item->rows_in;
      item->cols_in=(double)item->cols_in*style.magnify;
      item->rows_in=(double)item->rows_in*style.magnify;
    }

    /* how big will rotated text be ? */
    item->cols_out=fabs((double)item->rows_in*sin_angle) +
      fabs((double)item->cols_in*cos_angle) +0.99999 +2;

    item->rows_out=fabs((double)item->rows_in*cos_angle) +
      fabs((double)item->cols_in*sin_angle) +0.99999 +2;

    if(item->cols_out%2==0)
      item->cols_out++;

    if(item->rows_out%2==0)
      item->rows_out++;

    /* create image to hold rotated text */
    item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out);
    if(item->ximage==NULL)
      return NULL;

    byte_w_in=(item->cols_in-1)/8+1;
    byte_w_out=(item->cols_out-1)/8+1;

    /* we try to make this bit as fast as possible - which is why it looks
       a bit over-the-top */

    /* vertical distance from centre */
    dj=0.5-(double)item->rows_out/2;

    /* where abouts does text actually lie in rotated image? */
    /* check angle within 0.5 degrees (0.008 radians) */
    if(fabs((double)angle)<0.008 || fabs((double)angle-M_PI/2)<0.008 ||
       fabs((double)angle-M_PI)<0.008 || fabs((double)angle-3*M_PI/2)<0.008) {
      xl=0;
      xr=(double)item->cols_out;
      xinc=0;
    }
    else if(angle<M_PI) {
      xl=(double)item->cols_out/2+
          (dj-(double)item->rows_in/(2*cos_angle))/
          tan(angle)-2;
      xr=(double)item->cols_out/2+
          (dj+(double)item->rows_in/(2*cos_angle))/
          tan(angle)+2;
      xinc=1./tan(angle);
    }
    else {
      xl=(double)item->cols_out/2+
          (dj+(double)item->rows_in/(2*cos_angle))/
          tan(angle)-2;
      xr=(double)item->cols_out/2+
          (dj-(double)item->rows_in/(2*cos_angle))/
          tan(angle)+2;

      xinc=1./tan(angle);
    }

    /* loop through all relevent bits in rotated image */
    for(j=0; j<item->rows_out; j++) {

      /* no point re-calculating these every pass */
      di=(double)((xl<0)?0:(int)xl)+0.5-(double)item->cols_out/2;
      byte_out=(item->rows_out-j-1)*byte_w_out;

      /* loop through meaningful columns */
      for(i=((xl<0)?0:(int)xl);
          i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++) {

          /* rotate coordinates */
          itd=(double)item->cols_in/2 + ( di*cos_angle + dj*sin_angle);
          jtd=(double)item->rows_in/2 - (-di*sin_angle + dj*cos_angle);
          it = itd - (itd < 0); /* (int) -0.5 == 0 */
          jt = jtd - (jtd < 0);

          /* set pixel if required */
          if(it>=0 && it<item->cols_in && jt>=0 && jt<item->rows_in)
            if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0)
                item->ximage->data[byte_out+i/8]|=128>>i%8;

          di+=1;
      }
      dj+=1;
      xl+=xinc;
      xr+=xinc;
    }
    XDestroyImage(I_in);

    if(style.magnify!=1.) {
      item->cols_in=old_cols_in;
      item->rows_in=old_rows_in;
    }


#ifdef CACHE_BITMAPS

    /* create a bitmap to hold rotated text */
    item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
                         item->cols_out, item->rows_out, 1);

    /* make the text bitmap from XImage */
    XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0,
            item->cols_out, item->rows_out);

    XDestroyImage(item->ximage);

#endif /*CACHE_BITMAPS*/

    XFreeGC(dpy, font_gc);
    XFreePixmap(dpy, canvas);

    return item;
}


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


/**************************************************************************/
/*  Adds a text item to the end of the cache, removing as many items      */
/*    from the front as required to keep cache size below limit     */
/**************************************************************************/

static void XRotAddToLinkedList(Display *dpy, RotatedTextItem *item)
{

    static long int current_size=0;
    static RotatedTextItem *last=NULL;
    RotatedTextItem *i1=first_text_item, *i2=NULL;

#ifdef CACHE_BITMAPS

    /* I don't know how much memory a pixmap takes in the server -
       probably this + a bit more we can't account for */

    item->size=((item->cols_out-1)/8+1)*item->rows_out;

#else

    /* this is pretty much the size of a RotatedTextItem */

    item->size=((item->cols_out-1)/8+1)*item->rows_out +
      sizeof(XImage) + strlen(item->text) +
      item->nl*8*sizeof(double) + sizeof(RotatedTextItem);

    if(item->font_name!=NULL)
      item->size+=strlen(item->font_name);
    else
      item->size+=sizeof(Font);

#endif /*CACHE_BITMAPS */

#ifdef DEBUG
    /* count number of items in cache, for debugging */
    {
      int i=0;

      while(i1) {
          i++;
          i1=i1->next;
      }
      DEBUG_PRINT2("Cache has %d items.\n", i);
      i1=first_text_item;
    }
#endif

    DEBUG_PRINT4("current cache size=%ld, new item=%ld, limit=%d\n",
             current_size, item->size, CACHE_SIZE_LIMIT*1024);

    /* if this item is bigger than whole cache, forget it */
    if(item->size>CACHE_SIZE_LIMIT*1024) {
      DEBUG_PRINT1("Too big to cache\n\n");
      item->cached=0;
      return;
    }

    /* remove elements from cache as needed */
    while(i1 && current_size+item->size>CACHE_SIZE_LIMIT*1024) {

      DEBUG_PRINT2("Removed %ld bytes\n", i1->size);

      if(i1->font_name!=NULL)
          DEBUG_PRINT5("  (`%s'\n   %s\n   angle=%f align=%d)\n",
                   i1->text, i1->font_name, i1->angle, i1->align);

#ifdef CACHE_FID
      if(i1->font_name==NULL)
          DEBUG_PRINT5("  (`%s'\n  FID=%ld\n   angle=%f align=%d)\n",
                   i1->text, i1->fid, i1->angle, i1->align);
#endif /*CACHE_FID*/

      current_size-=i1->size;

      i2=i1->next;

      /* free resources used by the unlucky item */
      XRotFreeTextItem(dpy, i1);

      /* remove it from linked list */
      first_text_item=i2;
      i1=i2;
    }

    /* add new item to end of linked list */
    if(first_text_item==NULL) {
      item->next=NULL;
      first_text_item=item;
      last=item;
    }
    else {
      item->next=NULL;
      last->next=item;
      last=item;
    }

    /* new cache size */
    current_size+=item->size;

    item->cached=1;

    DEBUG_PRINT1("Added item to cache.\n");
}


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


/**************************************************************************/
/*  Free the resources used by a text item                          */
/**************************************************************************/

static void XRotFreeTextItem(Display *dpy, RotatedTextItem *item)
{
    free(item->text);

    if(item->font_name!=NULL)
      free(item->font_name);

    free((char *)item->corners_x);
    free((char *)item->corners_y);

#ifdef CACHE_BITMAPS
    XFreePixmap(dpy, item->bitmap);
#else
    XDestroyImage(item->ximage);
#endif /* CACHE_BITMAPS */

    free((char *)item);
}


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


/**************************************************************************/
/* Magnify an XImage using bilinear interpolation                   */
/**************************************************************************/

static XImage *XRotMagnifyImage(Display *dpy, XImage *ximage)
{
    int i, j;
    double x, y;
    double u,t;
    XImage *I_out;
    int cols_in, rows_in;
    int cols_out, rows_out;
    register int i2, j2;
    double z1, z2, z3, z4;
    int byte_width_in, byte_width_out;
    double mag_inv;

    /* size of input image */
    cols_in=ximage->width;
    rows_in=ximage->height;

    /* size of final image */
    cols_out=(double)cols_in*style.magnify;
    rows_out=(double)rows_in*style.magnify;

    /* this will hold final image */
    I_out=MakeXImage(dpy, cols_out, rows_out);
    if(I_out==NULL)
      return NULL;

    /* width in bytes of input, output images */
    byte_width_in=(cols_in-1)/8+1;
    byte_width_out=(cols_out-1)/8+1;

    /* for speed */
    mag_inv=1./style.magnify;

    y=0.;

    /* loop over magnified image */
    for(j2=0; j2<rows_out; j2++) {
      x=0;
      j=y;

      for(i2=0; i2<cols_out; i2++) {
          i=x;

          /* bilinear interpolation - where are we on bitmap ? */
          /* right edge */
          if(i==cols_in-1 && j!=rows_in-1) {
            t=0;
            u=y-(double)j;

            z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
            z2=z1;
            z3=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
            z4=z3;
          }
          /* top edge */
          else if(i!=cols_in-1 && j==rows_in-1) {
            t=x-(double)i;
            u=0;

            z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
            z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
            z3=z2;
            z4=z1;
          }
          /* top right corner */
          else if(i==cols_in-1 && j==rows_in-1) {
            u=0;
            t=0;

            z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
            z2=z1;
            z3=z1;
            z4=z1;
          }
          /* somewhere `safe' */
          else {
            t=x-(double)i;
            u=y-(double)j;

            z1=(ximage->data[j*byte_width_in+i/8] & 128>>(i%8))>0;
            z2=(ximage->data[j*byte_width_in+(i+1)/8] & 128>>((i+1)%8))>0;
            z3=(ximage->data[(j+1)*byte_width_in+(i+1)/8] &
                128>>((i+1)%8))>0;
            z4=(ximage->data[(j+1)*byte_width_in+i/8] & 128>>(i%8))>0;
          }

          /* if interpolated value is greater than 0.5, set bit */
          if(((1-t)*(1-u)*z1 + t*(1-u)*z2 + t*u*z3 + (1-t)*u*z4)>0.5)
            I_out->data[j2*byte_width_out+i2/8]|=128>>i2%8;

          x+=mag_inv;
      }
      y+=mag_inv;
    }

    /* destroy original */
    XDestroyImage(ximage);

    /* return big image */
    return I_out;
}



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


/**************************************************************************/
/* Calculate the bounding box some text will have when painted            */
/**************************************************************************/

XPoint *XRotTextExtents(Display *dpy, XFontStruct *font, double angle,
                  int x, int y, const char *text, int align)
{
    register int i;
    char *str1, *str2, *str3;
    char *str2_a="\0", *str2_b="\n\0";
    int height;
    double sin_angle, cos_angle;
    int nl, max_width;
    int cols_in, rows_in;
    double hot_x, hot_y;
    XPoint *xp_in, *xp_out;
    int dir, asc, desc;
    XCharStruct overall;

    /* manipulate angle to 0<=angle<360 degrees */
    while(angle<0)
      angle+=360;

    while(angle>360)
      angle-=360;

    angle *= DEG2RAD;

    /* count number of sections in string */
    nl=1;
    if(align!=NONE)
      for(i=strlen(text)-2; i >= 0; i--)
          if(text[i]=='\n')
            nl++;

    /* ignore newline characters if not doing alignment */
    if(align==NONE)
      str2=str2_a;
    else
      str2=str2_b;

    /* find width of longest section */
    str1=strdup(text);
    if(str1==NULL)
      return NULL;

    str3=strtok(str1, str2);

    XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
             &overall);

    max_width=overall.rbearing;

    /* loop through each section */
    do {
      str3=strtok((char *)NULL, str2);

      if(str3!=NULL) {
          XTextExtents(font, str3, strlen(str3), &dir, &asc, &desc,
                   &overall);

          if(overall.rbearing>max_width)
            max_width=overall.rbearing;
      }
    }
    while(str3!=NULL);

    free(str1);

    /* overall font height */
    height=font->ascent+font->descent;

    /* dimensions horizontal text will have */
    cols_in=max_width;
    rows_in=nl*height;

    /* pre-calculate sin and cos */
    sin_angle = myround(sin(angle)*1000.0) / 1000.0;
    cos_angle = myround(cos(angle)*1000.0) / 1000.0;

    /* y position */
    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
      hot_y=(double)rows_in/2*style.magnify;
    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
      hot_y=0;
    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
      hot_y= -(double)rows_in/2*style.magnify;
    else
      hot_y= -((double)rows_in/2-(double)font->descent)*style.magnify;

    /* x position */
    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
      hot_x= -(double)max_width/2*style.magnify;
    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
      hot_x=0;
    else
      hot_x=(double)max_width/2*style.magnify;

    /* reserve space for XPoints */
    xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
    if(!xp_in)
      return NULL;

    xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
    if(!xp_out)
      return NULL;

    /* bounding box when horizontal, relative to bitmap centre */
    xp_in[0].x= -(double)cols_in*style.magnify/2-style.bbx_pad;
    xp_in[0].y= (double)rows_in*style.magnify/2+style.bbx_pad;
    xp_in[1].x= (double)cols_in*style.magnify/2+style.bbx_pad;
    xp_in[1].y= (double)rows_in*style.magnify/2+style.bbx_pad;
    xp_in[2].x= (double)cols_in*style.magnify/2+style.bbx_pad;
    xp_in[2].y= -(double)rows_in*style.magnify/2-style.bbx_pad;
    xp_in[3].x= -(double)cols_in*style.magnify/2-style.bbx_pad;
    xp_in[3].y=-(double)rows_in*style.magnify/2-style.bbx_pad;
    xp_in[4].x=xp_in[0].x;
    xp_in[4].y=xp_in[0].y;

    /* rotate and translate bounding box */
    for(i=0; i<5; i++) {
      xp_out[i].x=(double)x + ( ((double)xp_in[i].x-hot_x)*cos_angle +
                          ((double)xp_in[i].y+hot_y)*sin_angle);
      xp_out[i].y=(double)y + (-((double)xp_in[i].x-hot_x)*sin_angle +
                         ((double)xp_in[i].y+hot_y)*cos_angle);
    }

    free((char *)xp_in);

    return xp_out;
}


#ifdef USE_FONTSET
static XFontStruct * RXFontStructOfFontSet(XFontSet font)
{
    char **ml;
    XFontStruct **fs_list;
    XFontsOfFontSet(font, &fs_list, &ml);
    return fs_list[0];
}

static int
XmbRotPaintAlignedString(Display *dpy, XFontSet font,
                   double angle, Drawable drawable, GC gc, int x, int y,
                   const char *text, int align);
static int
XmbRotDrawHorizontalString(Display *dpy, XFontSet font, Drawable drawable,
                     GC gc, int x, int y, const char *text, int align);
static RotatedTextItem
*XmbRotRetrieveFromCache(Display *dpy, XFontSet font, double angle, const char *text,
                   int align);
static RotatedTextItem
*XmbRotCreateTextItem(Display *dpy, XFontSet font, double angle, const char *text,
                  int align);

static int
XRfTextExtents(XFontSet font_set, const char *string, int num_bytes,
             XRectangle *overall_ink_return,
             XRectangle *overall_logical_return)
{
#ifdef HAVE_XUTF8TEXTEXTENTS
  if (utf8locale)
    return Xutf8TextExtents(font_set,string,num_bytes,
                      overall_ink_return, overall_logical_return);
#endif
  return XmbTextExtents(font_set,string,num_bytes,
                  overall_ink_return, overall_logical_return);
}

static void
XRfDrawString(Display *display, Drawable d, XFontSet font_set, GC gc,
            int x, int y, const char *string, int num_bytes)
{
#ifdef HAVE_XUTF8DRAWSTRING
    if (utf8locale)
      Xutf8DrawString(display, d, font_set, gc, x, y, string, num_bytes);
    else
#endif
      XmbDrawString(display, d, font_set, gc, x, y, string, num_bytes);
}

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


/**************************************************************************/
/*  A front end to XmbRotPaintAlignedString:                        */
/*    -no alignment, no background                            */
/**************************************************************************/


static int XmbRotDrawString(Display *dpy, XFontSet fontset, double angle,
                      Drawable drawable, GC gc, int x, int y, const char *str)
{
    return (XmbRotPaintAlignedString(dpy, fontset, angle, drawable, gc,
                             x, y, str, NONE));
}



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


/**************************************************************************/
/*  Aligns and paints a rotated string                              */
/**************************************************************************/

static int
XmbRotPaintAlignedString(Display *dpy, XFontSet font, double angle,
                   Drawable drawable, GC gc, int x, int y,
                   const char *text, int align)
{
    GC my_gc;
    int xp, yp;
    double hot_x, hot_y;
    double hot_xp, hot_yp;
    double sin_angle, cos_angle;
    RotatedTextItem *item;
    Pixmap bitmap_to_paint;
    XFontStruct *fs;

    /* return early for NULL/empty strings */
    if(text==NULL || *text=='\0')
      return 0;

    if(strlen(text)==0)
      return 0;

    /* manipulate angle to 0<=angle<360 degrees */
    while(angle<0)
      angle+=360;

    while(angle>=360)
      angle-=360;

    angle *= DEG2RAD;

    /* horizontal text made easy */
    if(angle==0. && style.magnify==1.)
      return(XmbRotDrawHorizontalString(dpy, font, drawable, gc, x, y,
                                text, align));

    /* get a rotated bitmap */
    item=XmbRotRetrieveFromCache(dpy, font, angle, text, align);
    if(item==NULL)
      return 0;

    /* this gc has similar properties to the user's gc */
    my_gc=XCreateGC(dpy, drawable, (unsigned long)0, 0);
    XCopyGC(dpy, gc, GCForeground|GCBackground|GCFunction|GCPlaneMask
          |GCClipMask,
          my_gc);

    /* alignment : which point (hot_x, hot_y) relative to bitmap centre
       coincides with user's specified point? */

    /* y position */
    fs = RXFontStructOfFontSet(font);

    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
      hot_y=(double)item->rows_in/2*style.magnify;
    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
      hot_y=0;
    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
      hot_y= -(double)item->rows_in/2*style.magnify;
    else
      hot_y= -((double)item->rows_in/2-(double)fs->descent)*style.magnify;

    /* x position */
    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
      hot_x= -(double)item->max_width/2*style.magnify;
    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
      hot_x=0;
    else
      hot_x=(double)item->max_width/2*style.magnify;

    /* pre-calculate sin and cos */
    sin_angle = myround(sin(angle)*1000.0) / 1000.0;
    cos_angle = myround(cos(angle)*1000.0) / 1000.0;

    /* rotate hot_x and hot_y around bitmap centre */
    hot_xp = hot_x*cos_angle - hot_y*sin_angle;
    hot_yp = hot_x*sin_angle + hot_y*cos_angle;

    /* where should top left corner of bitmap go ? */
    xp=(double)x-((double)item->cols_out/2 +hot_xp);
    yp=(double)y-((double)item->rows_out/2 -hot_yp);

    /* by default we draw the rotated bitmap, solid */
    bitmap_to_paint=item->bitmap;

    /* handle user stippling */
#ifndef X11R3
    {
      GC depth_one_gc;
      XGCValues values;
      Pixmap new_bitmap, inverse;

      /* try and get some GC properties */
      if(XGetGCValues(dpy, gc,
                  GCStipple|GCFillStyle|GCForeground|GCBackground|
                  GCTileStipXOrigin|GCTileStipYOrigin,
                  &values)) {

          /* only do this if stippling requested */
          if((values.fill_style==FillStippled ||
            values.fill_style==FillOpaqueStippled)) {

            /* opaque stipple: draw rotated text in background colour */
            if(values.fill_style==FillOpaqueStippled) {
                XSetForeground(dpy, my_gc, values.background);
                XSetFillStyle(dpy, my_gc, FillStippled);
                XSetStipple(dpy, my_gc, item->bitmap);
                XSetTSOrigin(dpy, my_gc, xp, yp);
                XFillRectangle(dpy, drawable, my_gc, xp, yp,
                           item->cols_out, item->rows_out);
                XSetForeground(dpy, my_gc, values.foreground);
            }

            /* this will merge the rotated text and the user's stipple */
            new_bitmap=XCreatePixmap(dpy, drawable,
                               item->cols_out, item->rows_out, 1);

            /* create a GC */
            depth_one_gc=XCreateGC(dpy, new_bitmap, (unsigned long)0, 0);
            XSetForeground(dpy, depth_one_gc, 1);
            XSetBackground(dpy, depth_one_gc, 0);

            /* set the relative stipple origin */
            XSetTSOrigin(dpy, depth_one_gc,
                       values.ts_x_origin-xp, values.ts_y_origin-yp);

            /* fill the whole bitmap with the user's stipple */
            XSetStipple(dpy, depth_one_gc, values.stipple);
            XSetFillStyle(dpy, depth_one_gc, FillOpaqueStippled);
            XFillRectangle(dpy, new_bitmap, depth_one_gc,
                         0, 0, item->cols_out, item->rows_out);

            /* set stipple origin back to normal */
            XSetTSOrigin(dpy, depth_one_gc, 0, 0);

            /* this will contain an inverse copy of the rotated text */
            inverse=XCreatePixmap(dpy, drawable,
                              item->cols_out, item->rows_out, 1);

            /* invert text */
            XSetFillStyle(dpy, depth_one_gc, FillSolid);
            XSetFunction(dpy, depth_one_gc, GXcopyInverted);
            XCopyArea(dpy, item->bitmap, inverse, depth_one_gc,
                    0, 0, item->cols_out, item->rows_out, 0, 0);

            /* now delete user's stipple everywhere EXCEPT on text */
            XSetForeground(dpy, depth_one_gc, 0);
            XSetBackground(dpy, depth_one_gc, 1);
            XSetStipple(dpy, depth_one_gc, inverse);
            XSetFillStyle(dpy, depth_one_gc, FillStippled);
            XSetFunction(dpy, depth_one_gc, GXcopy);
            XFillRectangle(dpy, new_bitmap, depth_one_gc,
                         0, 0, item->cols_out, item->rows_out);

            /* free resources */
            XFreePixmap(dpy, inverse);
            XFreeGC(dpy, depth_one_gc);

            /* this is the new bitmap */
            bitmap_to_paint=new_bitmap;
          }
      }
    }
#endif /*X11R3*/

    /* paint text using stipple technique */
    XSetFillStyle(dpy, my_gc, FillStippled);
    XSetStipple(dpy, my_gc, bitmap_to_paint);
    XSetTSOrigin(dpy, my_gc, xp, yp);
    XFillRectangle(dpy, drawable, my_gc, xp, yp,
               item->cols_out, item->rows_out);

    /* free our resources */
    XFreeGC(dpy, my_gc);

    /* stippled bitmap no longer needed */
    if(bitmap_to_paint!=item->bitmap)
      XFreePixmap(dpy, bitmap_to_paint);

#ifdef CACHE_XIMAGES
    XFreePixmap(dpy, item->bitmap);
#endif /*CACHE_XIMAGES*/

    /* if item isn't cached, destroy it completely */
    if(!item->cached)
      XRotFreeTextItem(dpy,item);

    /* we got to the end OK! */
    return 0;
}


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


/**************************************************************************/
/*  Draw a horizontal string in a quick fashion                     */
/**************************************************************************/

static int XmbRotDrawHorizontalString(Display *dpy, XFontSet font,
                              Drawable drawable, GC gc, int x, int y,
                              const char *text, int align)
{
    GC my_gc;
    int nl=1, i;
    int height;
    int xp, yp;
    char *str1, *str2, *str3;
    char *str2_a="\0", *str2_b="\n\0";

    if (text == NULL || *text=='\0') {
      DEBUG_PRINT1("Empty string, ignoring\n");
      return 0;
    }

    /* this gc has similar properties to the user's gc (including stipple) */
    my_gc=XCreateGC(dpy, drawable, (unsigned long)0, 0);
    XCopyGC(dpy, gc,
          GCForeground|GCBackground|GCFunction|GCStipple|GCFillStyle|
          GCTileStipXOrigin|GCTileStipYOrigin|GCPlaneMask|GCClipMask, my_gc);

    /* count number of sections in string */
    if(align!=NONE)
      for(i=strlen(text)-2; i >= 0; i--)
          if(text[i]=='\n')
            nl++;

    /* ignore newline characters if not doing alignment */
    if(align==NONE)
      str2=str2_a;
    else
      str2=str2_b;

    /* overall font height */
    height
      = RXFontStructOfFontSet(font)->ascent
      + RXFontStructOfFontSet(font)->descent;

    /* y position */
    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
      yp=y+RXFontStructOfFontSet(font)->ascent;
    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
      yp=y-nl*height/2+RXFontStructOfFontSet(font)->ascent;
    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
      yp=y-nl*height+RXFontStructOfFontSet(font)->ascent;
    else
      yp=y;

    str1 = strdup(text);
    if(str1 == NULL) return 1;

    str3=strtok(str1, str2);

    /* loop through each section in the string */
    do {

      XRectangle    r_ink, r_log;
      XRfTextExtents(font, str3, strlen(str3), &r_ink, &r_log);

      /* where to draw section in x ? */
      if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
          xp=x;
      else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
          xp=x-r_log.width/2;
      else
          xp=x-r_log.width;

      /* draw string onto bitmap */
      XRfDrawString(dpy, drawable, font, my_gc, xp, yp, str3, strlen(str3));

      /* move to next line */
      yp+=height;

      str3=strtok((char *)NULL, str2);
    }
    while(str3!=NULL);

    free(str1);
    XFreeGC(dpy, my_gc);

    return 0;
}


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


/**************************************************************************/
/*   Query cache for a match with this font/text/angle/alignment    */
/*     request, otherwise arrange for its creation                  */
/**************************************************************************/

static RotatedTextItem
*XmbRotRetrieveFromCache(Display *dpy, XFontSet font,
                   double angle, const char *text,
                   int align)
{
    Font fid;
    char *font_name=NULL;
    unsigned long name_value;
    RotatedTextItem *item=NULL;
    RotatedTextItem *i1=first_text_item;

    /* get font name, if it exists */
    if(XGetFontProperty(RXFontStructOfFontSet(font), XA_FONT, &name_value)) {
      DEBUG_PRINT1("got font name OK\n");
      font_name=XGetAtomName(dpy, name_value);
      fid=0;
    }
#ifdef CACHE_FID
    /* otherwise rely (unreliably?) on font ID */
    else {
      DEBUG_PRINT1("can't get fontname, caching FID\n");
      font_name=NULL;
      fid=font->fid;
    }
#else
    /* not allowed to cache font ID's */
    else {
      DEBUG_PRINT1("can't get fontname, can't cache\n");
      font_name=NULL;
      fid=0;
    }
#endif /*CACHE_FID*/

    /* look for a match in cache */

    /* matching formula:
       identical text;
       identical fontname (if defined, font ID's if not);
       angles close enough (<0.0001 here, could be smaller);
       HORIZONTAL alignment matches, OR it's a one line string;
       magnifications the same */

    while(i1 && !item) {
      /* match everything EXCEPT fontname/ID */
      if(strcmp(text, i1->text)==0 &&
         fabs(angle-i1->angle)<0.0001 &&
         style.magnify==i1->magnify &&
         (i1->nl==1 ||
          ((align==0)?9:(align-1))%3==
            ((i1->align==0)?9:(i1->align-1))%3)) {

          /* now match fontname/ID */
          if(font_name!=NULL && i1->font_name!=NULL) {
            if(strcmp(font_name, i1->font_name)==0) {
                item=i1;
                DEBUG_PRINT1("Matched against font names\n");
            }
            else
                i1=i1->next;
          }
#ifdef CACHE_FID
          else if(font_name==NULL && i1->font_name==NULL) {
            if(fid==i1->fid) {
                item=i1;
                DEBUG_PRINT1("Matched against FID's\n");
            }
            else
                i1=i1->next;
          }
#endif /*CACHE_FID*/
          else
            i1=i1->next;
      }
      else
          i1=i1->next;
    }

    if(item)
      DEBUG_PRINT1("**Found target in cache.\n");
    if(!item)
      DEBUG_PRINT1("**No match in cache.\n");

    /* no match */
    if(!item) {
      /* create new item */
      item=XmbRotCreateTextItem(dpy, font, angle, text, align);
      if(!item)
          return NULL;

      /* record what it shows */
      item->text=strdup(text);

      /* fontname or ID */
      if(font_name!=NULL) {
          item->font_name=strdup(font_name);
          item->fid=0;
      }
      else {
          item->font_name=NULL;
          item->fid=fid;
      }

      item->angle=angle;
      item->align=align;
      item->magnify=style.magnify;

      /* cache it */
      XRotAddToLinkedList(dpy, item);
    }

    if(font_name)
      XFree(font_name);

    /* if XImage is cached, need to recreate the bitmap */

#ifdef CACHE_XIMAGES
    {
      GC depth_one_gc;

      /* create bitmap to hold rotated text */
      item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
                           item->cols_out, item->rows_out, 1);

      /* depth one gc */
      depth_one_gc=XCreateGC(dpy, item->bitmap, (unsigned long)0, 0);
      XSetBackground(dpy, depth_one_gc, 0);
      XSetForeground(dpy, depth_one_gc, 1);

      /* make the text bitmap from XImage */
      XPutImage(dpy, item->bitmap, depth_one_gc, item->ximage, 0, 0, 0, 0,
              item->cols_out, item->rows_out);

      XFreeGC(dpy, depth_one_gc);
    }
#endif /*CACHE_XIMAGES*/

    return item;
}


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


/**************************************************************************/
/*  Create a rotated text item                                      */
/**************************************************************************/

static RotatedTextItem
*XmbRotCreateTextItem(Display *dpy, XFontSet font,
                  double angle, const char *text, int align)
{
    RotatedTextItem *item=NULL;
    Pixmap canvas;
    GC font_gc;
    XImage *I_in;
    register int i, j;
    char *str1, *str2, *str3;
    char *str2_a="\0", *str2_b="\n\0";
    int height;
    int byte_w_in, byte_w_out;
    int xp, yp;
    double sin_angle, cos_angle;
    int it, jt;
    double itd, jtd;
    double di, dj;
    int ic=0;
    double xl, xr, xinc;
    int byte_out;
    XRectangle    r_ink, r_log;
    int old_cols_in=0, old_rows_in=0;

    /* allocate memory */
    item=(RotatedTextItem *)malloc((unsigned)sizeof(RotatedTextItem));
    if(!item)
      return NULL;

    /* count number of sections in string */
    item->nl=1;
    if(align!=NONE)
      for(i=strlen(text)-2; i >= 0; i--)
          if(text[i]=='\n')
            item->nl++;

    /* ignore newline characters if not doing alignment */
    if(align==NONE)
      str2=str2_a;
    else
      str2=str2_b;

    /* find width of longest section */
    str1=strdup(text);
    if(str1==NULL)
      return NULL;

    str3=strtok(str1, str2);

    XRfTextExtents(font, str3, strlen(str3), &r_ink, &r_log);

    item->max_width=r_log.width;

    /* loop through each section */
    do {
      str3=strtok((char *)NULL, str2);

      if(str3!=NULL) {
        XRfTextExtents(font, str3, strlen(str3), &r_ink, &r_log);

          if(r_log.width>item->max_width)
            item->max_width=r_log.width;
      }
    }
    while(str3!=NULL);

    free(str1);

    /* overall font height */
    height=RXFontStructOfFontSet(font)->ascent
      +RXFontStructOfFontSet(font)->descent;

    /* dimensions horizontal text will have */
    item->cols_in=item->max_width;
    item->rows_in=item->nl*height;

    /* fudge in case one of the above is zero: */
    if (!item->cols_in) item->cols_in=1;
    if (!item->rows_in) item->rows_in=1;

    /* bitmap for drawing on */
    canvas=XCreatePixmap(dpy, DefaultRootWindow(dpy),
                   item->cols_in, item->rows_in, 1);

    /* create a GC for the bitmap */
    font_gc=XCreateGC(dpy, canvas, (unsigned long)0, 0);
    XSetBackground(dpy, font_gc, 0);

    /* make sure the bitmap is blank */
    XSetForeground(dpy, font_gc, 0);
    XFillRectangle(dpy, canvas, font_gc, 0, 0,
               item->cols_in+1, item->rows_in+1);
    XSetForeground(dpy, font_gc, 1);

    /* pre-calculate sin and cos */
    sin_angle = myround(sin(angle)*1000.0) / 1000.0;
    cos_angle = myround(cos(angle)*1000.0) / 1000.0;

    /* text background will be drawn using XFillPolygon */
    item->corners_x=
      (double *)malloc((unsigned)(4*item->nl*sizeof(double)));
    if(!item->corners_x)
      return NULL;

    item->corners_y=
      (double *)malloc((unsigned)(4*item->nl*sizeof(double)));
    if(!item->corners_y)
      return NULL;

    /* draw text horizontally */

    /* start at top of bitmap */
    yp=RXFontStructOfFontSet(font)->ascent;

    str1=strdup(text);
    if(str1==NULL)
      return NULL;

    str3=strtok(str1, str2);

    /* loop through each section in the string */
    do {
      XRectangle    r_ink, r_log;
      XRfTextExtents(font, str3, strlen(str3), &r_ink, &r_log);

      /* where to draw section in x ? */
      if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
          xp=0;
      else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
          xp=(item->max_width-r_log.width)/2;
      else
          xp=item->max_width-r_log.width;

      /* draw string onto bitmap */
      XRfDrawString(dpy, canvas, font, font_gc, xp, yp, str3, strlen(str3));

      /* keep a note of corner positions of this string */
      item->corners_x[ic]=((double)xp-(double)item->cols_in/2)*style.magnify;
      item->corners_y[ic]=((double)(yp-RXFontStructOfFontSet(font)->ascent)-
                       (double)item->rows_in/2) *style.magnify;
      item->corners_x[ic+1]=item->corners_x[ic];
      item->corners_y[ic+1]=item->corners_y[ic]+(double)height*style.magnify;
      item->corners_x[item->nl*4-1-ic]=item->corners_x[ic]+
          (double)r_log.width*style.magnify;
      item->corners_y[item->nl*4-1-ic]=item->corners_y[ic];
      item->corners_x[item->nl*4-2-ic]=
          item->corners_x[item->nl*4-1-ic];
      item->corners_y[item->nl*4-2-ic]=item->corners_y[ic+1];

      ic+=2;

      /* move to next line */
      yp+=height;

      str3=strtok((char *)NULL, str2);
    }
    while(str3!=NULL);

    free(str1);

    /* create image to hold horizontal text */
    I_in=MakeXImage(dpy, item->cols_in, item->rows_in);
    if(I_in==NULL)
      return NULL;

    /* extract horizontal text */
    XGetSubImage(dpy, canvas, 0, 0, item->cols_in, item->rows_in,
             1, XYPixmap, I_in, 0, 0);
    I_in->format=XYBitmap;

    /* magnify horizontal text */
    if(style.magnify!=1.) {
      I_in=XRotMagnifyImage(dpy, I_in);

      old_cols_in=item->cols_in;
      old_rows_in=item->rows_in;
      item->cols_in=(double)item->cols_in*style.magnify;
      item->rows_in=(double)item->rows_in*style.magnify;
    }

    /* how big will rotated text be ? */
    item->cols_out=fabs((double)item->rows_in*sin_angle) +
      fabs((double)item->cols_in*cos_angle) +0.99999 +2;

    item->rows_out=fabs((double)item->rows_in*cos_angle) +
      fabs((double)item->cols_in*sin_angle) +0.99999 +2;

    if(item->cols_out%2==0)
      item->cols_out++;

    if(item->rows_out%2==0)
      item->rows_out++;

    /* create image to hold rotated text */
    item->ximage=MakeXImage(dpy, item->cols_out, item->rows_out);
    if(item->ximage==NULL)
      return NULL;

    byte_w_in=(item->cols_in-1)/8+1;
    byte_w_out=(item->cols_out-1)/8+1;

    /* we try to make this bit as fast as possible - which is why it looks
       a bit over-the-top */

    /* vertical distance from centre */
    dj=0.5-(double)item->rows_out/2;

    /* where abouts does text actually lie in rotated image? */
    /* check angle within 0.5 degrees (0.008 radians) */
    if(fabs((double)angle)<0.008 || fabs((double)angle-M_PI/2)<0.008 ||
       fabs((double)angle-M_PI)<0.008 || fabs((double)angle-3*M_PI/2)<0.008 ||
       fabs((double)angle-2*M_PI)<0.008) {
      xl=0;
      xr=(double)item->cols_out;
      xinc=0;
    }
    else if(angle<M_PI) {
      xl=(double)item->cols_out/2+
          (dj-(double)item->rows_in/(2*cos_angle))/
          tan(angle)-2;
      xr=(double)item->cols_out/2+
          (dj+(double)item->rows_in/(2*cos_angle))/
          tan(angle)+2;
      xinc=1./tan(angle);
    }
    else {
      xl=(double)item->cols_out/2+
          (dj+(double)item->rows_in/(2*cos_angle))/
          tan(angle)-2;
      xr=(double)item->cols_out/2+
          (dj-(double)item->rows_in/(2*cos_angle))/
          tan(angle)+2;

      xinc=1./tan(angle);
    }

    /* loop through all relevent bits in rotated image */
    for(j=0; j<item->rows_out; j++) {

      /* no point re-calculating these every pass */
      di=(double)((xl<0)?0:(int)xl)+0.5-(double)item->cols_out/2;
      byte_out=(item->rows_out-j-1)*byte_w_out;

      /* loop through meaningful columns */
      for(i=((xl<0)?0:(int)xl);
          i<((xr>=item->cols_out)?item->cols_out:(int)xr); i++) {

          /* rotate coordinates */
          itd=(double)item->cols_in/2 + ( di*cos_angle + dj*sin_angle);
          jtd=(double)item->rows_in/2 - (-di*sin_angle + dj*cos_angle);
          it = itd - (itd < 0); /* (int) -0.5 == 0 */
          jt = jtd - (jtd < 0);

          /* set pixel if required */
          if(it>=0 && it<item->cols_in && jt>=0 && jt<item->rows_in)
            if((I_in->data[jt*byte_w_in+it/8] & 128>>(it%8))>0)
                item->ximage->data[byte_out+i/8]|=128>>i%8;

          di+=1;
      }
      dj+=1;
      xl+=xinc;
      xr+=xinc;
    }
    XDestroyImage(I_in);

    if(style.magnify!=1.) {
      item->cols_in=old_cols_in;
      item->rows_in=old_rows_in;
    }


#ifdef CACHE_BITMAPS

    /* create a bitmap to hold rotated text */
    item->bitmap=XCreatePixmap(dpy, DefaultRootWindow(dpy),
                         item->cols_out, item->rows_out, 1);

    /* make the text bitmap from XImage */
    XPutImage(dpy, item->bitmap, font_gc, item->ximage, 0, 0, 0, 0,
            item->cols_out, item->rows_out);

    XDestroyImage(item->ximage);

#endif /*CACHE_BITMAPS*/

    XFreeGC(dpy, font_gc);
    XFreePixmap(dpy, canvas);

    return item;
}


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


/**************************************************************************/
/* Calculate the bounding box some text will have when painted            */
/**************************************************************************/

XPoint *XmbRotTextExtents(Display *dpy, XFontSet font, double angle,
                    int x, int y, const char *text, int align)
{
    register int i;
    char *str1, *str2, *str3;
    char *str2_a="\0", *str2_b="\n\0";
    int height;
    double sin_angle, cos_angle;
    int nl, max_width;
    int cols_in, rows_in;
    double hot_x, hot_y;
    XPoint *xp_in, *xp_out;
    XRectangle    r_ink, r_log;

    /* manipulate angle to 0<=angle<360 degrees */
    while(angle<0)
      angle+=360;

    while(angle>360)
      angle-=360;

    angle *= DEG2RAD;

    /* count number of sections in string */
    nl=1;
    if(align!=NONE)
      for(i=strlen(text)-2; i >= 0; i--)
          if(text[i]=='\n')
            nl++;

    /* ignore newline characters if not doing alignment */
    if(align==NONE)
      str2=str2_a;
    else
      str2=str2_b;

    /* find width of longest section */
    str1=strdup(text);
    if(str1==NULL)
      return NULL;

    str3=strtok(str1, str2);

    XRfTextExtents(font, str3, strlen(str3), &r_ink, &r_log);

    max_width=r_log.width;

    /* loop through each section */
    do {
      str3=strtok((char *)NULL, str2);

      if(str3!=NULL) {
        XRfTextExtents(font, str3, strlen(str3), &r_ink, &r_log);

          if(r_log.width>max_width)
            max_width=r_log.width;
      }
    }
    while(str3!=NULL);

    free(str1);

    /* overall font height */
    height=RXFontStructOfFontSet(font)->ascent
      +RXFontStructOfFontSet(font)->descent;

    /* dimensions horizontal text will have */
    cols_in=max_width;
    rows_in=nl*height;

    /* pre-calculate sin and cos */
    sin_angle = myround(sin(angle)*1000.0) / 1000.0;
    cos_angle = myround(cos(angle)*1000.0) / 1000.0;

    /* y position */
    if(align==TLEFT || align==TCENTRE || align==TRIGHT)
      hot_y=(double)rows_in/2*style.magnify;
    else if(align==MLEFT || align==MCENTRE || align==MRIGHT)
      hot_y=0;
    else if(align==BLEFT || align==BCENTRE || align==BRIGHT)
      hot_y= -(double)rows_in/2*style.magnify;
    else
      hot_y= -((double)rows_in/2-
             (double)RXFontStructOfFontSet(font)->descent)*style.magnify;

    /* x position */
    if(align==TLEFT || align==MLEFT || align==BLEFT || align==NONE)
      hot_x= -(double)max_width/2*style.magnify;
    else if(align==TCENTRE || align==MCENTRE || align==BCENTRE)
      hot_x=0;
    else
      hot_x=(double)max_width/2*style.magnify;

    /* reserve space for XPoints */
    xp_in=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
    if(!xp_in)
      return NULL;

    xp_out=(XPoint *)malloc((unsigned)(5*sizeof(XPoint)));
    if(!xp_out)
      return NULL;

    /* bounding box when horizontal, relative to bitmap centre */
    xp_in[0].x= -(double)cols_in*style.magnify/2-style.bbx_pad;
    xp_in[0].y= (double)rows_in*style.magnify/2+style.bbx_pad;
    xp_in[1].x= (double)cols_in*style.magnify/2+style.bbx_pad;
    xp_in[1].y= (double)rows_in*style.magnify/2+style.bbx_pad;
    xp_in[2].x= (double)cols_in*style.magnify/2+style.bbx_pad;
    xp_in[2].y= -(double)rows_in*style.magnify/2-style.bbx_pad;
    xp_in[3].x= -(double)cols_in*style.magnify/2-style.bbx_pad;
    xp_in[3].y=-(double)rows_in*style.magnify/2-style.bbx_pad;
    xp_in[4].x=xp_in[0].x;
    xp_in[4].y=xp_in[0].y;

    /* rotate and translate bounding box */
    for(i=0; i<5; i++) {
      xp_out[i].x=(double)x + ( ((double)xp_in[i].x-hot_x)*cos_angle +
                          ((double)xp_in[i].y+hot_y)*sin_angle);
      xp_out[i].y=(double)y + (-((double)xp_in[i].x-hot_x)*sin_angle +
                         ((double)xp_in[i].y+hot_y)*cos_angle);
    }

    free((char *)xp_in);

    return xp_out;
}
#endif

Generated by  Doxygen 1.6.0   Back to index