text: 
  /* 
  A* -------------------------------------------------------------------
  B* This file contains source code for the PyMOL computer program
  C* copyright 1998-2003 by Warren Lyford Delano of DeLano Scientific. 
  D* -------------------------------------------------------------------
  E* It is unlawful to modify or remove this copyright notice.
  F* -------------------------------------------------------------------
  G* Please see the accompanying LICENSE file for further information. 
  H* --------------------------------------------------\-----------------
  I* Additional authors of this source file include:
  -* 
  -* 
  -*
  Z* -------------------------------------------------------------------
  */
  #include"os_python.h"
  
  #include "MemoryDebug.h"
  #include "OOMac.h"
  #include "os_gl.h"
  #include "FontType.h"
  #include "Text.h"
  #include "Ortho.h"
  #include "Scene.h"
  #include "Character.h"
  #include "Util.h"
  #include "TypeFace.h"
  
  typedef struct {
    CFont Font;                   /* must be first */
    PyMOLGlobals *G;
    CTypeFace *TypeFace;
  } CFontType;
  
  #ifdef _PYMOL_INLINE
  __inline__
  #endif
  static char *_FontTypeRenderOpenGL(RenderInfo * info,
                                     CFontType * I, char *st,
                                     float size, int flat, float *rpos SHADERCGOARG)
  {
    register PyMOLGlobals *G = I->Font.G;
    if(G->ValidContext) {
      unsigned int c;
      int pushed = OrthoGetPushed(G);
      int kern_flag = false;
      unsigned int last_c = 0;
      int sampling = 1;
      const float _0 = 0.0F, _1 = 1.0F, _m1 = -1.0F;
      float x_indent = 0.0F, y_indent = 0.0F, z_indent = 0.0F;
      float text_width = 0.f;
      int unicode = 0;
      int unicnt = 0;
  
      sampling = info->sampling;
      if(st && (*st)) {
        float origin[3], v_scale;
        float screenWorldOffset[3] = { 0.0F, 0.0F, 0.0F };
        
        SceneOriginGet(G, origin);
        v_scale = SceneGetScreenVertexScale(G, origin);
        if(size < _0) {
          size = (int) (0.5F - size / v_scale);
        }
  
        if(rpos) {
          if(rpos[0] < _1) {      /* we need to measure the string width before starting to draw */
            char *sst = st;
            while((c = *(sst++))) {
              if(unicnt) {
                if(!(c & 0x80))   /* corrupt UTF8 */
                  unicnt = 0;
                else {
                  unicode = (unicode << 6) | (0x3F & c);
                  unicnt--;
                  c = unicode;
                }
              } else if(c & 0x80) {
                while(c & 0x80) {
                  c = (c << 1) & 0xFF;
                  unicnt++;
                }
                unicode = (c >> (unicnt--));
              }
              if(!unicnt) {
                CharFngrprnt fprnt;
                unsigned char *rgba;
                UtilZeroMem(&fprnt, sizeof(fprnt));
                fprnt.u.i.text_id = I->Font.TextID;
                fprnt.u.i.size = (int) (size * 64 * sampling);
                rgba = fprnt.u.i.color;
                TextGetColorUChar(G, rgba, rgba + 1, rgba + 2, rgba + 3);
                rgba = fprnt.u.i.outline_color;
                if(!flat) {
                  TextGetOutlineColor(G, rgba, rgba + 1, rgba + 2, rgba + 3);
                } else {
                  TextGetColorUChar(G, rgba, rgba + 1, rgba + 2, rgba + 3);
                }
                fprnt.u.i.ch = c;
                fprnt.u.i.flat = flat;
                {
                  int id = CharacterFind(G, &fprnt);
                  if(!id) {
                    id = TypeFaceCharacterNew(I->TypeFace, &fprnt, size * sampling);
                  }
                  if(id) {
                    if(kern_flag) {
                      text_width += (TypeFaceGetKerning(I->TypeFace,
  							       last_c, c, size) / sampling);
                    }
                    text_width += CharacterGetAdvance(G, sampling, id);
                  }
                }
                kern_flag = true;
                last_c = c;
              }
            }
  	  {
  	    float factor = rpos[0] / 2.0F - 0.5F;
  	    /* if -1. < rpos[0] < 1. , then we need to determine the label's width
  	       so that we can justify it appropriately */
  	    if(factor < _m1) // if rpos[0] < -1., right justified
  	      factor = -_1;
  	    if(factor > _0)  // if rpos[0] > 1., left justified, label width not needed
  	      factor = _0;
  	    x_indent -= factor * text_width;
  	  }
          }
  	/* if label_position x is -1 to 1, the label is placed in x such that
  	   0 - centered 
  	   -1 - right justified on projected point
  	    1 - left justified on projected point
  	*/
  	if(rpos[0] < _m1) {
  	  screenWorldOffset[0] -= (rpos[0] + _1);// / v_scale;
  	} else if(rpos[0] > _1) {
  	  screenWorldOffset[0] -= (rpos[0] - _1);// / v_scale;
  	}
          if(rpos[1] < _1) {
            float factor = -rpos[1] / 2.0F + 0.5F;
            if(factor > _1)
              factor = _1;
            if(factor < _0)
              factor = _0;
            y_indent = 0.75 * size * factor;
          }
          if(rpos[1] < _m1) {
            screenWorldOffset[1] -= (rpos[1] + _1);
          } else if(rpos[1] > _1) {
            screenWorldOffset[1] -= (rpos[1] - _1);
          }
          z_indent = rpos[2];
          if(z_indent < _0) {     /* leave room for fonts of finite depth */
            z_indent += _1;
            if(z_indent > _0)
              z_indent = _0;
          } else if(z_indent > _0) {
            z_indent -= _1;
            if(z_indent < _0)
              z_indent = _0;
          }
  	if (!shaderCGO){
  	  x_indent += screenWorldOffset[0] / v_scale;
  	  y_indent += screenWorldOffset[1] / v_scale;
  	} else {
            screenWorldOffset[2] += rpos[2];
  	}
        }
        if(!pushed) {
          float *v = TextGetPos(G);
          float loc[3];
          float zero[3] = { 0.0F, 0.0F, 0.0F };
  	TextSetScreenWorldOffset(G, screenWorldOffset);
          TextSetWorldPos(G, v);
          if(rpos) {
            if(info->ortho) {
              float orig[3];
              SceneOriginGet(G, orig);
              SceneGetEyeNormal(G, orig, loc);
            } else {
              SceneGetEyeNormal(G, v, loc);
            }
            scale3f(loc, z_indent, loc);
            add3f(v, loc, loc);
            v = loc;
          }
          ScenePushRasterMatrix(G, v);
          TextSetPos(G, zero);
        }
        if(rpos) {
  	//	float ax = x_indent * v_scale, ay = y_indent * v_scale;
  	//	printf("x_indent=%f y_indent=%f ax=%f ay=%f v_scale=%f rpos=%f %f %f\n", x_indent, y_indent, ax, ay, v_scale, rpos[0], rpos[1], rpos[2]);
          TextIndent(G, x_indent, y_indent);
        }
        CharacterRenderOpenGLPrime(G, info);
        while((c = *(st++))) {
          if(unicnt) {
            if(!(c & 0x80))       /* corrupt UTF8 */
              unicnt = 0;
            else {
              unicode = (unicode << 6) | (0x3F & c);
              unicnt--;
              c = unicode;
            }
          } else if(c & 0x80) {
            while(c & 0x80) {
              c = (c << 1) & 0xFF;
              unicnt++;
            }
            unicode = (c >> (unicnt--));
          }
          if(!unicnt) {
            CharFngrprnt fprnt;
            unsigned char *rgba;
            UtilZeroMem(&fprnt, sizeof(fprnt));
            fprnt.u.i.text_id = I->Font.TextID;
            fprnt.u.i.size = (int) (size * 64 * sampling);
            rgba = fprnt.u.i.color;
            TextGetColorUChar(G, rgba, rgba + 1, rgba + 2, rgba + 3);
            rgba = fprnt.u.i.outline_color;
            TextGetOutlineColor(G, rgba, rgba + 1, rgba + 2, rgba + 3);
            fprnt.u.i.ch = c;
            fprnt.u.i.flat = flat;
            {
              int id = CharacterFind(G, &fprnt);
              if(!id) {
                id = TypeFaceCharacterNew(I->TypeFace, &fprnt, size * sampling);
              }
              if(id) {
                if(kern_flag) {
                  TextAdvance(G, TypeFaceGetKerning(I->TypeFace,
                                                    last_c, c, size) / sampling);
                }
                CharacterRenderOpenGL(G, info, id, true SHADERCGOARGVAR);       /* handles advance */
              }
            }
            kern_flag = true;
            last_c = c;
          }
        }
        CharacterRenderOpenGLDone(G, info);
        if(!pushed) {
          ScenePopRasterMatrix(G);
        }
      }
    }
    return st;
  }
  
  static char *FontTypeRenderOpenGL(RenderInfo * info, CFontType * I, char *st, float size,
                                    float *rpos SHADERCGOARG)
  {
    return _FontTypeRenderOpenGL(info, I, st, size, false, rpos SHADERCGOARGVAR);
  }
  
  static char *FontTypeRenderOpenGLFlat(RenderInfo * info, CFontType * I, char *st,
                                        float size, float *rpos SHADERCGOARG)
  {
    return _FontTypeRenderOpenGL(info, I, st, size, true, rpos SHADERCGOARGVAR);
  }
  
  static char *FontTypeRenderRay(CRay * ray, CFontType * I, char *st, float size,
                                 float *rpos)
  {
    register PyMOLGlobals *G = I->Font.G;
    unsigned int c;
    int kern_flag = false;
    unsigned int last_c = 0;
    int sampling = ray->Sampling;
    const float _0 = 0.0F, _1 = 1.0F, _m1 = -1.0F;
    float x_indent = 0.0F, y_indent = 0.0F, z_indent = 0.0F;
    float xn[3], yn[3], x_adj[3], y_adj[3], pos[3], *v;
    int unicode = 0;
    int unicnt = 0;
  
    if(st && (*st)) {
      float origin[3], v_scale;
      SceneOriginGet(G, origin);
      v_scale = SceneGetScreenVertexScale(G, origin);
  
      if(rpos) {
        float loc[3];
        z_indent = rpos[2];
        if(z_indent < _0) {       /* leave room for fonts of finite depth */
          z_indent += _1;
          if(z_indent > _0)
            z_indent = _0;
        } else if(z_indent > _0) {
          z_indent -= _1;
          if(z_indent < _0)
            z_indent = _0;
        }
  
        v = TextGetPos(I->G);
  
        if(ray->Ortho) {
          float orig[3];
          SceneOriginGet(G, orig);
          SceneGetEyeNormal(G, orig, loc);
        } else {
          SceneGetEyeNormal(G, v, loc);
        }
        scale3f(loc, z_indent, loc);
        add3f(v, loc, loc);
        TextSetPos(I->G, loc);
      }
  
      RayGetScaledAxes(ray, xn, yn);
  
      if(size < _0) {
  
        size = (int) (0.5F - size / v_scale);
      }
  
      if(rpos) {
  
        if(rpos[0] < _1) {        /* we need to measure the string width before starting to draw */
          float factor = rpos[0] / 2.0F - 0.5F;
          char *sst = st;
          if(factor < _m1)
            factor = -_1;
          if(factor > _0)
            factor = _0;
          while((c = *(sst++))) {
            if(unicnt) {
              if(!(c & 0x80))     /* corrupt UTF8 */
                unicnt = 0;
              else {
                unicode = (unicode << 6) | (0x3F & c);
                unicnt--;
                c = unicode;
              }
            } else if(c & 0x80) {
              while(c & 0x80) {
                c = (c << 1) & 0xFF;
                unicnt++;
              }
              unicode = (c >> (unicnt--));
            }
            if(!unicnt) {
              CharFngrprnt fprnt;
              unsigned char *rgba;
              UtilZeroMem(&fprnt, sizeof(fprnt));
              fprnt.u.i.text_id = I->Font.TextID;
              fprnt.u.i.size = (int) (size * 64 * sampling);
              rgba = fprnt.u.i.color;
              TextGetColorUChar(G, rgba, rgba + 1, rgba + 2, rgba + 3);
              rgba = fprnt.u.i.outline_color;
              TextGetOutlineColor(G, rgba, rgba + 1, rgba + 2, rgba + 3);
              fprnt.u.i.ch = c;
              {
                int id = CharacterFind(G, &fprnt);
                if(!id) {
                  id = TypeFaceCharacterNew(I->TypeFace, &fprnt, size * sampling);
                }
                if(id) {
                  if(kern_flag) {
                    x_indent -= factor * TypeFaceGetKerning(I->TypeFace,
                                                            last_c,
                                                            c, size * sampling) / sampling;
                  }
                  x_indent -= factor * CharacterGetAdvance(G, 1, id);
                  kern_flag = true;
                  last_c = c;
                }
              }
            }
          }
        }
        if(rpos[0] < _m1) {
          x_indent -= 2 * (rpos[0] + _1) / v_scale;
        } else if(rpos[0] > _1) {
          x_indent -= 2 * (rpos[0] - _1) / v_scale;
        }
        if(rpos[1] < _1) {
          float factor = -rpos[1] / 2.0F + 0.5F;
          if(factor > _1)
            factor = _1;
          if(factor < _0)
            factor = _0;
          y_indent = 0.75F * sampling * size * factor;
        }
        if(rpos[1] < _m1) {
          y_indent -= 2 * (rpos[1] + _1) / v_scale;
        } else if(rpos[1] > _1) {
          y_indent -= 2 * (rpos[1] - _1) / v_scale;
        }
        v = TextGetPos(I->G);
        scale3f(xn, x_indent, x_adj);
        scale3f(yn, y_indent, y_adj);
        subtract3f(v, x_adj, pos);
        subtract3f(pos, y_adj, pos);
        TextSetPos(I->G, pos);
      }
      kern_flag = false;
  
      while((c = *(st++))) {
        if(unicnt) {
          if(!(c & 0x80))         /* corrupt UTF8 */
            unicnt = 0;
          else {
            unicode = (unicode << 6) | (0x3F & c);
            unicnt--;
            c = unicode;
          }
        } else if(c & 0x80) {
          while(c & 0x80) {
            c = (c << 1) & 0xFF;
            unicnt++;
          }
          unicode = (c >> (unicnt--));
        }
        if(!unicnt) {
          CharFngrprnt fprnt;
          unsigned char *rgba;
          UtilZeroMem(&fprnt, sizeof(fprnt));
          fprnt.u.i.text_id = I->Font.TextID;
          fprnt.u.i.size = (int) (size * 64 * sampling);
          rgba = fprnt.u.i.color;
          TextGetColorUChar(G, rgba, rgba + 1, rgba + 2, rgba + 3);
          rgba = fprnt.u.i.outline_color;
          TextGetOutlineColor(G, rgba, rgba + 1, rgba + 2, rgba + 3);
          fprnt.u.i.ch = c;
          {
            int id = CharacterFind(G, &fprnt);
            if(!id) {
              id = TypeFaceCharacterNew(I->TypeFace, &fprnt, size * sampling);
            }
            if(id) {
              if(kern_flag) {
                float kern = TypeFaceGetKerning(I->TypeFace,
                                                last_c,
                                                c,
                                                size * sampling) / sampling;
                v = TextGetPos(I->G);
                scale3f(xn, kern, x_adj);
                add3f(v, x_adj, pos);
                TextSetPos(I->G, pos);
              }
              ray->fCharacter(ray, id);   /* handles advance */
  
              kern_flag = true;
              last_c = c;
            }
          }
        }
      }
    }
    return st;
  }
  
  static void FontTypeFree(CFont * font)
  {
    register CFontType *I = (CFontType *) font;
    TypeFaceFree(I->TypeFace);
    OOFreeP(I);
  }
  
  CFont *FontTypeNew(PyMOLGlobals * G, unsigned char *dat, unsigned int len)
  {
    OOAlloc(G, CFontType);
    FontInit(G, &I->Font);
    I->G = G;
    I->Font.fRenderOpenGL = (FontRenderOpenGLFn *) FontTypeRenderOpenGL;
    I->Font.fRenderOpenGLFlat = (FontRenderOpenGLFn *) FontTypeRenderOpenGLFlat;
    I->Font.fRenderRay = (FontRenderRayFn *) FontTypeRenderRay;
    I->Font.fFree = FontTypeFree;
    I->TypeFace = TypeFaceLoad(G, dat, len);
    if(!I->TypeFace) {
      OOFreeP(I);
    }
    return (CFont *) I;
  }
  