Logo Search packages:      
Sourcecode: matplotlib version File versions  Download package

ft2font.cpp

#include <sstream>
#include "ft2font.h"
#include "mplutils.h"

#define FIXED_MAJOR(val) (*((short *) &val+1))
#define FIXED_MINOR(val) (*((short *) &val+0))

/**
 To improve the hinting of the fonts, this code uses a hack
 presented here:
 
 http://antigrain.com/research/font_rasterization/index.html
 
 The idea is to limit the effect of hinting in the x-direction, while
 preserving hinting in the y-direction.  Since freetype does not
 support this directly, the dpi in the x-direction is set higher than
 in the y-direction, which affects the hinting grid.  Then, a global
 transform is placed on the font to shrink it back to the desired
 size.  While it is a bit surprising that the dpi setting affects
 hinting, whereas the global transform does not, this is documented
 behavior of freetype, and therefore hopefully unlikely to change.
 The freetype 2 tutorial says:
 
      NOTE: The transformation is applied to every glyph that is
      loaded through FT_Load_Glyph and is completely independent of
      any hinting process. This means that you won't get the same
      results if you load a glyph at the size of 24 pixels, or a glyph
      at the size at 12 pixels scaled by 2 through a transform,
      because the hints will have been computed differently (except
      you have disabled hints).

 This hack is enabled only when VERTICAL_HINTING is defined, and will
 only be effective when load_char and set_text are called with 'flags=
 LOAD_DEFAULT', which is the default.
 */
#define VERTICAL_HINTING
#ifdef VERTICAL_HINTING
#define HORIZ_HINTING 8
#else
#define HORIZ_HINTING 1
#endif

FT_Library _ft2Library;

// FT2Image::FT2Image() : 
//   _isDirty(true),
//   _buffer(NULL),
//   _width(0), _height(0),
//   _rgbCopy(NULL),
//   _rgbaCopy(NULL) {
//   _VERBOSE("FT2Image::FT2Image");
// }

FT2Image::FT2Image(unsigned long width, unsigned long height) :
  _isDirty(true),
  _buffer(NULL), 
  _width(0), _height(0),
  _rgbCopy(NULL),
  _rgbaCopy(NULL) {
  _VERBOSE("FT2Image::FT2Image");
  resize(width, height);
}

FT2Image::~FT2Image() { 
  _VERBOSE("FT2Image::~FT2Image");
  delete [] _buffer; 
  _buffer=NULL; 
  delete _rgbCopy;
  delete _rgbaCopy;
}

void FT2Image::resize(unsigned long width, unsigned long height) {
  size_t numBytes = width*height;

  if (width != _width || height != _height) {
    if (numBytes > _width*_height) {
      delete [] _buffer;
      _buffer = NULL;
      _buffer = new unsigned char [numBytes];
    }

    _width = width;
    _height = height;
  }

  memset(_buffer, 0, numBytes);

  _isDirty = true;
}

void
FT2Image::draw_bitmap( FT_Bitmap*  bitmap,
                   FT_Int      x,
                   FT_Int      y) {
  _VERBOSE("FT2Image::draw_bitmap");
  FT_Int image_width = (FT_Int)_width;
  FT_Int image_height = (FT_Int)_height;
  FT_Int char_width =  bitmap->width;
  FT_Int char_height = bitmap->rows;

  FT_Int x1 = CLAMP(x, 0, image_width);
  FT_Int y1 = CLAMP(y, 0, image_height);
  FT_Int x2 = CLAMP(x + char_width, 0, image_width);
  FT_Int y2 = CLAMP(y + char_height, 0, image_height);

  FT_Int x_start = MAX(0, -x);
  FT_Int y_offset = y1 - MAX(0, -y);

  for ( FT_Int i = y1; i < y2; ++i ) {
    unsigned char* dst = _buffer + (i * image_width + x1);
    unsigned char* src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start);
    for ( FT_Int j = x1; j < x2; ++j, ++dst, ++src )
      *dst |= *src;
  }

  _isDirty = true;
}

void FT2Image::write_bitmap(const char* filename) const {
  FILE *fh = fopen(filename, "w");

  for ( size_t i = 0; i< _height; i++) {
    for ( size_t j = 0; j < _width; ++j) {
      if (_buffer[j + i*_width])
      fputc('#', fh);
      else
      fputc(' ', fh);
    }
    fputc('\n', fh);
  }

  fclose(fh);
}

char FT2Image::write_bitmap__doc__[] =
"write_bitmap(fname)\n"
"\n"
"Write the bitmap to file fname\n"
;
Py::Object
FT2Image::py_write_bitmap(const Py::Tuple & args) {
  _VERBOSE("FT2Image::write_bitmap");

  args.verify_length(1);

  std::string filename = Py::String(args[0]);

  write_bitmap(filename.c_str());

  return Py::Object();
}

void
FT2Image::draw_rect(unsigned long x0, unsigned long y0, 
                unsigned long x1, unsigned long y1) {
  if ( x0<0 || y0<0 || x1<0 || y1<0 ||
       x0>_width || x1>_width ||
       y0>_height || y1>_height )
    throw Py::ValueError("Rect coords outside image bounds");

  size_t top = y0*_width;
  size_t bottom = y1*_width;
  for (size_t i=x0; i<x1+1; ++i) {
    _buffer[i + top] = 255;
    _buffer[i + bottom] = 255;
  }

  for (size_t j=y0+1; j<y1; ++j) {
    _buffer[x0 + j*_width] = 255;
    _buffer[x1 + j*_width] = 255;
  }

  _isDirty = true;
}

char FT2Image::draw_rect__doc__[] =
"draw_rect(x0, y0, x1, y1)\n"
"\n"
"Draw a rect to the image.\n"
"\n"
;
Py::Object
FT2Image::py_draw_rect(const Py::Tuple & args) {
  _VERBOSE("FT2Image::draw_rect");

  args.verify_length(4);

  long x0 = Py::Int(args[0]);
  long y0 = Py::Int(args[1]);
  long x1 = Py::Int(args[2]);
  long y1 = Py::Int(args[3]);

  draw_rect(x0, y0, x1, y1);

  return Py::Object();
}

void FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, 
                        unsigned long x1, unsigned long y1) {
  x0 = CLAMP(x0, 0, _width);
  y0 = CLAMP(y0, 0, _height);
  x1 = CLAMP(x1, 0, _width);
  y1 = CLAMP(y1, 0, _height);

  for (size_t j=y0; j<y1+1; j++) {
    for (size_t i=x0; i<x1+1; i++) {
      _buffer[i + j*_width] = 255;
    }
  }

  _isDirty = true;
}  

char FT2Image::draw_rect_filled__doc__[] =
"draw_rect_filled(x0, y0, x1, y1)\n"
"\n"
"Draw a filled rect to the image.\n"
"\n"
;
Py::Object
FT2Image::py_draw_rect_filled(const Py::Tuple & args) {
  _VERBOSE("FT2Image::draw_rect_filled");

  args.verify_length(4);

  long x0 = Py::Int(args[0]);
  long y0 = Py::Int(args[1]);
  long x1 = Py::Int(args[2]);
  long y1 = Py::Int(args[3]);

  draw_rect_filled(x0, y0, x1, y1);

  return Py::Object();
}

char FT2Image::as_str__doc__[] =
"width, height, s = image_as_str()\n"
"\n"
"Return the image buffer as a string\n"
"\n"
;
Py::Object
FT2Image::py_as_str(const Py::Tuple & args) {
  _VERBOSE("FT2Image::as_str");
  args.verify_length(0);

  return Py::asObject
    (PyString_FromStringAndSize((const char *)_buffer, 
                        _width*_height)
     );
}

void FT2Image::makeRgbCopy() {
  if (!_isDirty)
    return;

  if (!_rgbCopy) {
    _rgbCopy = new FT2Image(_width * 3, _height);
  } else {
    _rgbCopy->resize(_width * 3, _height);
  }
  unsigned char *src          = _buffer;
  unsigned char *src_end      = src + (_width * _height);
  unsigned char *dst          = _rgbCopy->_buffer;

  unsigned char tmp;
  while (src != src_end) {
    tmp = 255 - *src++;
    *dst++ = tmp;
    *dst++ = tmp;
    *dst++ = tmp;
  }
}

char FT2Image::as_rgb_str__doc__[] =
"width, height, s = image_as_rgb_str()\n"
"\n"
"Return the image buffer as a 24-bit RGB string.\n"
"\n"
;
Py::Object
FT2Image::py_as_rgb_str(const Py::Tuple & args) {
  _VERBOSE("FT2Image::as_str_rgb");
  args.verify_length(0);

  makeRgbCopy();
  
  return _rgbCopy->py_as_str(args);
}

void FT2Image::makeRgbaCopy() {
  if (!_isDirty)
    return;

  if (!_rgbaCopy) {
    _rgbaCopy = new FT2Image(_width * 4, _height);
  } else {
    _rgbaCopy->resize(_width * 4, _height);
  }
  unsigned char *src          = _buffer;
  unsigned char *src_end      = src + (_width * _height);
  unsigned char *dst          = _rgbaCopy->_buffer;

  while (src != src_end) {
    // We know the array has already been zero'ed out in
    // the resize method, so we just skip over the r, g and b.
    dst += 3;
    *dst++ = *src++;
  }
}

char FT2Image::as_rgba_str__doc__[] =
"width, height, s = image_as_rgb_str()\n"
"\n"
"Return the image buffer as a 32-bit RGBA string.\n"
"\n"
;
Py::Object
FT2Image::py_as_rgba_str(const Py::Tuple & args) {
  _VERBOSE("FT2Image::as_str_rgba");
  args.verify_length(0);

  makeRgbaCopy();
  
  return _rgbaCopy->py_as_str(args);
}

Py::Object
FT2Image::py_get_width(const Py::Tuple & args) {
  _VERBOSE("FT2Image::get_width");
  args.verify_length(0);

  return Py::Int((long)get_width());
}

Py::Object
FT2Image::py_get_height(const Py::Tuple & args) {
  _VERBOSE("FT2Image::get_height");
  args.verify_length(0);

  return Py::Int((long)get_height());
}

Glyph::Glyph( const FT_Face& face, const FT_Glyph& glyph, size_t ind) :
  glyphInd(ind) {
  _VERBOSE("Glyph::Glyph");

  FT_BBox bbox;
  FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_subpixels, &bbox );

  setattr("width",        Py::Int( face->glyph->metrics.width / HORIZ_HINTING) );
  setattr("height",       Py::Int( face->glyph->metrics.height) );
  setattr("horiBearingX", Py::Int( face->glyph->metrics.horiBearingX / HORIZ_HINTING) );
  setattr("horiBearingY", Py::Int( face->glyph->metrics.horiBearingY) );
  setattr("horiAdvance",  Py::Int( face->glyph->metrics.horiAdvance) );
  setattr("linearHoriAdvance",  Py::Int( face->glyph->linearHoriAdvance / HORIZ_HINTING) );
  setattr("vertBearingX", Py::Int( face->glyph->metrics.vertBearingX) );

  setattr("vertBearingY", Py::Int( face->glyph->metrics.vertBearingY) );
  setattr("vertAdvance",  Py::Int( face->glyph->metrics.vertAdvance) );
  //setattr("bitmap_left",  Py::Int( face->glyph->bitmap_left) );
  //setattr("bitmap_top",  Py::Int( face->glyph->bitmap_top) );

  Py::Tuple abbox(4);

  abbox[0] = Py::Int(bbox.xMin);
  abbox[1] = Py::Int(bbox.yMin);
  abbox[2] = Py::Int(bbox.xMax);
  abbox[3] = Py::Int(bbox.yMax);
  setattr("bbox", abbox);
  setattr("path", get_path(face));
}

Glyph::~Glyph() {
  _VERBOSE("Glyph::~Glyph");
}

int
Glyph::setattr( const char *name, const Py::Object &value ) {
  _VERBOSE("Glyph::setattr");
  __dict__[name] = value;
  return 0;
}

Py::Object
Glyph::getattr( const char *name ) {
  _VERBOSE("Glyph::getattr");
  if ( __dict__.hasKey(name) ) return __dict__[name];
  else return getattr_default( name );
}

inline double conv(int v)
{
  return double(v) / 64.0;
}


//see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html
Py::Object
Glyph::get_path( const FT_Face& face) {
  //get the glyph as a path, a list of (COMMAND, *args) as desribed in matplotlib.path
  // this code is from agg's decompose_ft_outline with minor modifications

  enum {MOVETO, LINETO, CURVE3, CURVE4, ENDPOLY};
  FT_Outline& outline = face->glyph->outline;
  Py::List path;
  bool flip_y= false; //todo, pass me as kwarg


  FT_Vector   v_last;
  FT_Vector   v_control;
  FT_Vector   v_start;

  FT_Vector*  point;
  FT_Vector*  limit;
  char*       tags;

  int   n;         // index of contour in outline
  int   first;     // index of first point in contour
  char  tag;       // current point's state

  first = 0;

  for(n = 0; n < outline.n_contours; n++)
    {
      int  last;  // index of last point in contour

      last  = outline.contours[n];
      limit = outline.points + last;

      v_start = outline.points[first];
      v_last  = outline.points[last];

      v_control = v_start;

      point = outline.points + first;
      tags  = outline.tags  + first;
      tag   = FT_CURVE_TAG(tags[0]);

      // A contour cannot start with a cubic control point!
      if(tag == FT_CURVE_TAG_CUBIC) return Py::Object();

      // check first point to determine origin
      if( tag == FT_CURVE_TAG_CONIC)
      {
        // first point is conic control.  Yes, this happens.
        if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON)
          {
            // start at last point if it is on the curve
            v_start = v_last;
            limit--;
          }
        else
          {
            // if both first and last points are conic,
            // start at their middle and record its position
            // for closure
            v_start.x = (v_start.x + v_last.x) / 2;
            v_start.y = (v_start.y + v_last.y) / 2;

            v_last = v_start;
          }
        point--;
        tags--;
      }

      double x = conv(v_start.x);
      double y = flip_y ? -conv(v_start.y) : conv(v_start.y);
      Py::Tuple tup(3);
      tup[0] = Py::Int(MOVETO);
      tup[1] = Py::Float(x);
      tup[2] = Py::Float(y);
      path.append(tup);

      Py::Tuple closepoly(2);
      closepoly[0] = Py::Int(ENDPOLY);
      closepoly[1] = Py::Int(0);

      while(point < limit)
      {
        point++;
        tags++;

        tag = FT_CURVE_TAG(tags[0]);
        switch(tag)
          {
          case FT_CURVE_TAG_ON:  // emit a single line_to
            {
            double x = conv(point->x);
            double y = flip_y ? -conv(point->y) : conv(point->y);
            Py::Tuple tup(3);
            tup[0] = Py::Int(LINETO);
            tup[1] = Py::Float(x);
            tup[2] = Py::Float(y);
            path.append(tup);

            continue;
            }

          case FT_CURVE_TAG_CONIC:  // consume conic arcs
            {
            v_control.x = point->x;
            v_control.y = point->y;

            Do_Conic:
            if(point < limit)
              {
                FT_Vector vec;
                FT_Vector v_middle;

                point++;
                tags++;
                tag = FT_CURVE_TAG(tags[0]);

                vec.x = point->x;
                vec.y = point->y;

                if(tag == FT_CURVE_TAG_ON)
                  {
                  double xctl = conv(v_control.x);
                  double yctl = flip_y ? -conv(v_control.y) : conv(v_control.y);
                  double xto = conv(vec.x);
                  double yto = flip_y ? -conv(vec.y) : conv(vec.y);
                  Py::Tuple tup(5);
                  tup[0] = Py::Int(CURVE3);
                  tup[1] = Py::Float(xctl);
                  tup[2] = Py::Float(yctl);
                  tup[3] = Py::Float(xto);
                  tup[4] = Py::Float(yto);
                  path.append(tup);
                  continue;
                  }

                if(tag != FT_CURVE_TAG_CONIC) return Py::Object();

                v_middle.x = (v_control.x + vec.x) / 2;
                v_middle.y = (v_control.y + vec.y) / 2;

                double xctl = conv(v_control.x);
                double yctl = flip_y ? -conv(v_control.y) : conv(v_control.y);
                double xto = conv(v_middle.x);
                double yto = flip_y ? -conv(v_middle.y) : conv(v_middle.y);
                Py::Tuple tup(5);
                tup[0] = Py::Int(CURVE3);
                tup[1] = Py::Float(xctl);
                tup[2] = Py::Float(yctl);
                tup[3] = Py::Float(xto);
                tup[4] = Py::Float(yto);
                path.append(tup);

                v_control = vec;
                goto Do_Conic;
              }
            double xctl = conv(v_control.x);
            double yctl = flip_y ? -conv(v_control.y) : conv(v_control.y);
            double xto = conv(v_start.x);
            double yto = flip_y ? -conv(v_start.y) : conv(v_start.y);
            Py::Tuple tup(5);
            tup[0] = Py::Int(CURVE3);
            tup[1] = Py::Float(xctl);
            tup[2] = Py::Float(yctl);
            tup[3] = Py::Float(xto);
            tup[4] = Py::Float(yto);
            path.append(tup);
            goto Close;
            }

          default:  // FT_CURVE_TAG_CUBIC
            {
            FT_Vector vec1, vec2;

            if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
              {
                return Py::Object();
              }

            vec1.x = point[0].x;
            vec1.y = point[0].y;
            vec2.x = point[1].x;
            vec2.y = point[1].y;

            point += 2;
            tags  += 2;

            if(point <= limit)
              {
                FT_Vector vec;

                vec.x = point->x;
                vec.y = point->y;

                double xctl1 = conv(vec1.x);
                double yctl1 = flip_y ? -conv(vec1.y) : conv(vec1.y);
                double xctl2 = conv(vec2.x);
                double yctl2 = flip_y ? -conv(vec2.y) : conv(vec2.y);
                double xto = conv(vec.x);
                double yto = flip_y ? -conv(vec.y) : conv(vec.y);
                Py::Tuple tup(7);
                tup[0] = Py::Int(CURVE4);
                tup[1] = Py::Float(xctl1);
                tup[2] = Py::Float(yctl1);
                tup[3] = Py::Float(xctl2);
                tup[4] = Py::Float(yctl2);
                tup[5] = Py::Float(xto);
                tup[6] = Py::Float(yto);
                path.append(tup);

                continue;
              }

            double xctl1 = conv(vec1.x);
            double yctl1 = flip_y ? -conv(vec1.y) : conv(vec1.y);
            double xctl2 = conv(vec2.x);
            double yctl2 = flip_y ? -conv(vec2.y) : conv(vec2.y);
            double xto = conv(v_start.x);
            double yto = flip_y ? -conv(v_start.y) : conv(v_start.y);
            Py::Tuple tup(7);
            tup[0] = Py::Int(CURVE4);
            tup[1] = Py::Float(xctl1);
            tup[2] = Py::Float(yctl1);
            tup[3] = Py::Float(xctl2);
            tup[4] = Py::Float(yctl2);
            tup[5] = Py::Float(xto);
            tup[6] = Py::Float(yto);
            path.append(tup);

            goto Close;
            }
          }
      }

      path.append(closepoly);


    Close:
      first = last + 1;
    }

  return path;
}


FT2Font::FT2Font(std::string facefile) :
  image(NULL)
{
  _VERBOSE(Printf("FT2Font::FT2Font %s", facefile.c_str()).str());
  clear(Py::Tuple(0));

  int error = FT_New_Face( _ft2Library, facefile.c_str(), 0, &face );


  if (error == FT_Err_Unknown_File_Format ) {
    std::ostringstream s;
    s << "Could not load facefile " << facefile << "; Unknown_File_Format" << std::endl;
    throw Py::RuntimeError(s.str());
  }
  else if (error == FT_Err_Cannot_Open_Resource) {
    std::ostringstream s;
    s << "Could not open facefile " << facefile << "; Cannot_Open_Resource" << std::endl;
    throw Py::RuntimeError(s.str());
  }
  else if (error == FT_Err_Invalid_File_Format) {
    std::ostringstream s;
    s << "Could not open facefile " << facefile << "; Invalid_File_Format" << std::endl;
    throw Py::RuntimeError(s.str());
  }
  else if (error) {
    std::ostringstream s;
    s << "Could not open facefile " << facefile << "; freetype error code " << error<< std::endl;
    throw Py::RuntimeError(s.str());
  }

  // set a default fontsize 12 pt at 72dpi
#ifdef VERTICAL_HINTING  
  error = FT_Set_Char_Size( face, 12 * 64, 0, 72 * HORIZ_HINTING, 72 );
  static FT_Matrix transform = { 65536 / HORIZ_HINTING, 0, 0, 65536 };
  FT_Set_Transform( face, &transform, 0 );
#else
  error = FT_Set_Char_Size( face, 12 * 64, 0, 72, 72 );
#endif
  //error = FT_Set_Char_Size( face, 20 * 64, 0, 80, 80 );
  if (error) {
    std::ostringstream s;
    s << "Could not set the fontsize for facefile  " << facefile << std::endl;
    throw Py::RuntimeError(s.str());
  }

  // set some face props as attributes
  //small memory leak fixed after 2.1.8
  //fields can be null so we have to check this first

  const char* ps_name = FT_Get_Postscript_Name( face );
  if ( ps_name == NULL )
    ps_name = "UNAVAILABLE";

   const char* family_name = face->family_name;
   if ( family_name == NULL )
     family_name = "UNAVAILABLE";

   const char* style_name = face->style_name;
   if ( style_name == NULL )
     style_name = "UNAVAILABLE";


  setattr("postscript_name", Py::String(ps_name));
  setattr("num_faces",       Py::Int(face->num_faces));
  setattr("family_name",     Py::String(family_name));
  setattr("style_name",      Py::String(style_name));
  setattr("face_flags",      Py::Int(face->face_flags));
  setattr("style_flags",     Py::Int(face->style_flags));
  setattr("num_glyphs",      Py::Int(face->num_glyphs));
  setattr("num_fixed_sizes", Py::Int(face->num_fixed_sizes));
  setattr("num_charmaps",    Py::Int(face->num_charmaps));

  int scalable = FT_IS_SCALABLE( face );

  setattr("scalable", Py::Int(scalable));

  if (scalable) {
    setattr("units_per_EM", Py::Int(face->units_per_EM));

    Py::Tuple bbox(4);
    bbox[0] = Py::Int(face->bbox.xMin);
    bbox[1] = Py::Int(face->bbox.yMin);
    bbox[2] = Py::Int(face->bbox.xMax);
    bbox[3] = Py::Int(face->bbox.yMax);
    setattr("bbox",  bbox);
    setattr("ascender",            Py::Int(face->ascender));
    setattr("descender",           Py::Int(face->descender));
    setattr("height",              Py::Int(face->height));
    setattr("max_advance_width",   Py::Int(face->max_advance_width));
    setattr("max_advance_height",  Py::Int(face->max_advance_height));
    setattr("underline_position",  Py::Int(face->underline_position));
    setattr("underline_thickness", Py::Int(face->underline_thickness));
  }

  setattr("fname", Py::String(facefile));

  _VERBOSE("FT2Font::FT2Font done");
}

FT2Font::~FT2Font()
{
  _VERBOSE("FT2Font::~FT2Font");

  Py_XDECREF(image);
  FT_Done_Face    ( face );

  for (size_t i=0; i<glyphs.size(); i++) {
    FT_Done_Glyph( glyphs[i] );
  }

  for (size_t i=0; i<gms.size(); i++) {
    Py_DECREF(gms[i]);
  }
}

int
FT2Font::setattr( const char *name, const Py::Object &value ) {
  _VERBOSE("FT2Font::setattr");
  __dict__[name] = value;
  return 1;
}

Py::Object
FT2Font::getattr( const char *name ) {
  _VERBOSE("FT2Font::getattr");
  if ( __dict__.hasKey(name) ) return __dict__[name];
  else return getattr_default( name );
}

char FT2Font::clear__doc__[] =
"clear()\n"
"\n"
"Clear all the glyphs, reset for a new set_text"
;

Py::Object
FT2Font::clear(const Py::Tuple & args) {
  _VERBOSE("FT2Font::clear");
  args.verify_length(0);

  Py_XDECREF(image);
  image = NULL;

  angle = 0.0;

  pen.x = 0;
  pen.y = 0;

  for (size_t i=0; i<glyphs.size(); i++) {
    FT_Done_Glyph( glyphs[i] );
  }

  for (size_t i=0; i<gms.size(); i++) {
    Py_DECREF(gms[i]);
  }

  glyphs.resize(0);
  gms.resize(0);

  return Py::Object();
}




char FT2Font::set_size__doc__[] =
"set_size(ptsize, dpi)\n"
"\n"
"Set the point size and dpi of the text.\n"
;

Py::Object
FT2Font::set_size(const Py::Tuple & args) {
  _VERBOSE("FT2Font::set_size");
  args.verify_length(2);

  double ptsize = Py::Float(args[0]);
  double dpi = Py::Float(args[1]);

#ifdef VERTICAL_HINTING
  int error = FT_Set_Char_Size( face, (long)(ptsize * 64), 0,
                        (unsigned int)dpi * HORIZ_HINTING,
                        (unsigned int)dpi );
  static FT_Matrix transform = { 65536 / HORIZ_HINTING, 0, 0, 65536 };
  FT_Set_Transform( face, &transform, 0 );
#else
  int error = FT_Set_Char_Size( face, (long)(ptsize * 64), 0,
                        (unsigned int)dpi,
                        (unsigned int)dpi );
#endif  
  if (error)
    throw Py::RuntimeError("Could not set the fontsize");
  return Py::Object();
}


char FT2Font::set_charmap__doc__[] =
"set_charmap(i)\n"
"\n"
"Make the i-th charmap current\n"
;

Py::Object
FT2Font::set_charmap(const Py::Tuple & args) {
  _VERBOSE("FT2Font::set_charmap");
  args.verify_length(1);

  int i = Py::Int(args[0]);
  if (i>=face->num_charmaps)
    throw Py::ValueError("i exceeds the available number of char maps");
  FT_CharMap charmap = face->charmaps[i];
  if (FT_Set_Charmap( face, charmap ))
    throw Py::ValueError("Could not set the charmap");
  return Py::Object();
}

FT_BBox
FT2Font::compute_string_bbox(  ) {
  _VERBOSE("FT2Font::compute_string_bbox");

  FT_BBox bbox;
  /* initialize string bbox to "empty" values */
  bbox.xMin = bbox.yMin = 32000;
  bbox.xMax = bbox.yMax = -32000;

  for ( size_t n = 0; n < glyphs.size(); n++ ) {
    FT_BBox glyph_bbox;
    FT_Glyph_Get_CBox( glyphs[n], ft_glyph_bbox_subpixels, &glyph_bbox );
    if ( glyph_bbox.xMin < bbox.xMin ) bbox.xMin = glyph_bbox.xMin;
    if ( glyph_bbox.yMin < bbox.yMin ) bbox.yMin = glyph_bbox.yMin;
    if ( glyph_bbox.xMax > bbox.xMax ) bbox.xMax = glyph_bbox.xMax;
    if ( glyph_bbox.yMax > bbox.yMax ) bbox.yMax = glyph_bbox.yMax;
  }
  /* check that we really grew the string bbox */
  if ( bbox.xMin > bbox.xMax ) {
    bbox.xMin = 0; bbox.yMin = 0; bbox.xMax = 0; bbox.yMax = 0;
  }
  return bbox;
}


char FT2Font::get_kerning__doc__[] =
"dx = get_kerning(left, right, mode)\n"
"\n"
"Get the kerning between left char and right glyph indices\n"
"mode is a kerning mode constant\n"
"  KERNING_DEFAULT  - Return scaled and grid-fitted kerning distances\n"
"  KERNING_UNFITTED - Return scaled but un-grid-fitted kerning distances\n"
"  KERNING_UNSCALED - Return the kerning vector in original font units\n"
;
Py::Object
FT2Font::get_kerning(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_kerning");
  args.verify_length(3);
  int left = Py::Int(args[0]);
  int right = Py::Int(args[1]);
  int mode = Py::Int(args[2]);


  if (!FT_HAS_KERNING( face )) return Py::Int(0);
  FT_Vector delta;

  if (!FT_Get_Kerning( face, left, right, mode, &delta )) {
    return Py::Int(delta.x / HORIZ_HINTING);
  }
  else {
    return Py::Int(0);

  }
}



char FT2Font::set_text__doc__[] =
"set_text(s, angle)\n"
"\n"
"Set the text string and angle.\n"
"You must call this before draw_glyphs_to_bitmap\n"
"A sequence of x,y positions is returned";
Py::Object
FT2Font::set_text(const Py::Tuple & args, const Py::Dict & kwargs) {
  _VERBOSE("FT2Font::set_text");
  args.verify_length(2);


  Py::String text( args[0] );
  std::string stdtext="";
  Py_UNICODE* pcode=NULL;
  size_t N = 0;
  if (PyUnicode_Check(text.ptr())) {
    pcode = PyUnicode_AsUnicode(text.ptr());
    N = PyUnicode_GetSize(text.ptr());
  }
  else {
    stdtext = text.as_std_string();
    N = stdtext.size();
  }


  angle = Py::Float(args[1]);

  angle = angle/360.0*2*3.14159;

  long flags = FT_LOAD_FORCE_AUTOHINT;
  if (kwargs.hasKey("flags"))
    flags = Py::Long(kwargs["flags"]);

  //this computes width and height in subpixels so we have to divide by 64
  matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
  matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
  matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
  matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );

  FT_Bool use_kerning = FT_HAS_KERNING( face );
  FT_UInt previous = 0;

  glyphs.resize(0);
  pen.x = 0;
  pen.y = 0;

  Py::Tuple xys(N);
  for ( unsigned int n = 0; n < N; n++ ) {
    std::string thischar("?");
    FT_UInt glyph_index;


    if (pcode==NULL) {
      // plain ol string
      thischar = stdtext[n];
      glyph_index = FT_Get_Char_Index( face, stdtext[n] );
     }
    else {
      //unicode
      glyph_index = FT_Get_Char_Index( face, pcode[n] );
    }

    // retrieve kerning distance and move pen position
    if ( use_kerning && previous && glyph_index ) {
      FT_Vector delta;
      FT_Get_Kerning( face, previous, glyph_index,
                  FT_KERNING_DEFAULT, &delta );
      pen.x += delta.x / HORIZ_HINTING;
    }
    error = FT_Load_Glyph( face, glyph_index, flags );
    if ( error ) {
      std::cerr << "\tcould not load glyph for " << thischar << std::endl;
      continue;
    }
    // ignore errors, jump to next glyph

    // extract glyph image and store it in our table

    FT_Glyph thisGlyph;
    error = FT_Get_Glyph( face->glyph, &thisGlyph );

    if ( error ) {
      std::cerr << "\tcould not get glyph for " << thischar << std::endl;
      continue;
    }
    // ignore errors, jump to next glyph

    FT_Glyph_Transform( thisGlyph, 0, &pen);
    Py::Tuple xy(2);
    xy[0] = Py::Float(pen.x);
    xy[1] = Py::Float(pen.y);
    xys[n] = xy;
    pen.x += face->glyph->advance.x;

    previous = glyph_index;
    glyphs.push_back(thisGlyph);
  }

  // now apply the rotation
  for (unsigned int n=0; n<glyphs.size(); n++)
    FT_Glyph_Transform(glyphs[n], &matrix, 0);

  _VERBOSE("FT2Font::set_text done");
  return xys;
}


char FT2Font::get_glyph__doc__[] =
"get_glyph(num)\n"
"\n"
"Return the glyph object with num num\n"
;
Py::Object
FT2Font::get_glyph(const Py::Tuple & args){
  _VERBOSE("FT2Font::get_glyph");

  args.verify_length(1);
  int num = Py::Int(args[0]);

  if ( (size_t)num >= gms.size())
    throw Py::ValueError("Glyph index out of range");

  Py_INCREF(gms[num]);
  return Py::asObject(gms[num]);
}

char FT2Font::get_num_glyphs__doc__[] =
"get_num_glyphs()\n"
"\n"
"Return the number of loaded glyphs\n"
;
Py::Object
FT2Font::get_num_glyphs(const Py::Tuple & args){
  _VERBOSE("FT2Font::get_num_glyphs");
  args.verify_length(0);

  return Py::Int((long)glyphs.size());
}

char FT2Font::load_char__doc__[] =
"load_char(charcode, flags=LOAD_FORCE_AUTOHINT)\n"
"\n"
"Load character with charcode in current fontfile and set glyph.\n"
"The flags argument can be a bitwise-or of the LOAD_XXX constants.\n"
"Return value is a Glyph object, with attributes\n"
"  width          # glyph width\n"
"  height         # glyph height\n"
"  bbox           # the glyph bbox (xmin, ymin, xmax, ymax)\n"
"  horiBearingX   # left side bearing in horizontal layouts\n"
"  horiBearingY   # top side bearing in horizontal layouts\n"
"  horiAdvance    # advance width for horizontal layout\n"
"  vertBearingX   # left side bearing in vertical layouts\n"
"  vertBearingY   # top side bearing in vertical layouts\n"
"  vertAdvance    # advance height for vertical layout\n"
;
Py::Object
FT2Font::load_char(const Py::Tuple & args, const Py::Dict & kwargs) {
  _VERBOSE("FT2Font::load_char");
  //load a char using the unsigned long charcode

  args.verify_length(1);
  long charcode = Py::Long(args[0]), flags = Py::Long(FT_LOAD_FORCE_AUTOHINT);
  if (kwargs.hasKey("flags"))
    flags = Py::Long(kwargs["flags"]);
  
  int error = FT_Load_Char( face, (unsigned long)charcode, flags);

  if (error)
    throw Py::RuntimeError(Printf("Could not load charcode %d", charcode).str());

  FT_Glyph thisGlyph;
  error = FT_Get_Glyph( face->glyph, &thisGlyph );

  if (error)
    throw Py::RuntimeError(Printf("Could not get glyph for char %d", charcode).str());

  size_t num = glyphs.size();  //the index into the glyphs list
  glyphs.push_back(thisGlyph);
  Glyph* gm = new Glyph(face, thisGlyph, num);
  gms.push_back(gm);
  Py_INCREF(gm); //todo: refcount correct?
  return Py::asObject( gm);
}

char FT2Font::get_width_height__doc__[] =
"w, h = get_width_height()\n"
"\n"
"Get the width and height in 26.6 subpixels of the current string set by set_text\n"
"The rotation of the string is accounted for.  To get width and height\n"
"in pixels, divide these values by 64\n"
;
Py::Object
FT2Font::get_width_height(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_width_height");
  args.verify_length(0);

  FT_BBox bbox = compute_string_bbox();

  Py::Tuple ret(2);
  ret[0] = Py::Int(bbox.xMax - bbox.xMin);
  ret[1] = Py::Int(bbox.yMax - bbox.yMin);
  return ret;
}

char FT2Font::get_descent__doc__[] =
"d = get_descent()\n"
"\n"
"Get the descent of the current string set by set_text in 26.6 subpixels.\n"
"The rotation of the string is accounted for.  To get the descent\n"
"in pixels, divide this value by 64.\n"
;
Py::Object
FT2Font::get_descent(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_descent");
  args.verify_length(0);

  FT_BBox bbox = compute_string_bbox();
  return Py::Int(- bbox.yMin);;
}

char FT2Font::draw_glyphs_to_bitmap__doc__[] =
"draw_glyphs_to_bitmap()\n"
"\n"
"Draw the glyphs that were loaded by set_text to the bitmap\n"
"The bitmap size will be automatically set to include the glyphs\n"
;
Py::Object
FT2Font::draw_glyphs_to_bitmap(const Py::Tuple & args) {

  _VERBOSE("FT2Font::draw_glyphs_to_bitmap");
  args.verify_length(0);

  FT_BBox string_bbox = compute_string_bbox();
  size_t width = (string_bbox.xMax-string_bbox.xMin) / 64 + 2;
  size_t height = (string_bbox.yMax-string_bbox.yMin) / 64 + 2;

  Py_XDECREF(image);
  image = NULL;
  image = new FT2Image(width, height);

  for ( size_t n = 0; n < glyphs.size(); n++ )
    {
      FT_BBox bbox;
      FT_Glyph_Get_CBox(glyphs[n], ft_glyph_bbox_pixels, &bbox);

      error = FT_Glyph_To_Bitmap(&glyphs[n],
                         ft_render_mode_normal,
                         0,
                         1
                         );
      if (error)
      throw Py::RuntimeError("Could not convert glyph to bitmap");

      FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n];
      // now, draw to our target surface (convert position)

      //bitmap left and top in pixel, string bbox in subpixel
      FT_Int x = (FT_Int)(bitmap->left - (string_bbox.xMin / 64.));
      FT_Int y = (FT_Int)((string_bbox.yMax / 64.) - bitmap->top + 1);

      image->draw_bitmap( &bitmap->bitmap, x, y);
    }

  return Py::Object();
}


char FT2Font::get_xys__doc__[] =
"get_xys()\n"
"\n"
"Get the xy locations of the current glyphs\n"
;
Py::Object
FT2Font::get_xys(const Py::Tuple & args) {

  _VERBOSE("FT2Font::get_xys");
  args.verify_length(0);

  FT_BBox string_bbox = compute_string_bbox();
  Py::Tuple xys(glyphs.size());

  for ( size_t n = 0; n < glyphs.size(); n++ )
    {

      FT_BBox bbox;
      FT_Glyph_Get_CBox(glyphs[n], ft_glyph_bbox_pixels, &bbox);

      error = FT_Glyph_To_Bitmap(&glyphs[n],
                         ft_render_mode_normal,
                         0,
                         1
                         );
      if (error)
      throw Py::RuntimeError("Could not convert glyph to bitmap");

      FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n];


      //bitmap left and top in pixel, string bbox in subpixel
      FT_Int x = (FT_Int)(bitmap->left-string_bbox.xMin/64.);
      FT_Int y = (FT_Int)(string_bbox.yMax/64.-bitmap->top+1);
      //make sure the index is non-neg
      x = x<0?0:x;
      y = y<0?0:y;
      Py::Tuple xy(2);
      xy[0] = Py::Float(x);
      xy[1] = Py::Float(y);
      xys[n] = xy;
    }

  return xys;
}

char FT2Font::draw_glyph_to_bitmap__doc__[] =
"draw_glyph_to_bitmap(bitmap, x, y, glyph)\n"
"\n"
"Draw a single glyph to the bitmap at pixel locations x,y\n"
"Note it is your responsibility to set up the bitmap manually\n"
"with set_bitmap_size(w,h) before this call is made.\n"
"\n"
"If you want automatic layout, use set_text in combinations with\n"
"draw_glyphs_to_bitmap.  This function is intended for people who\n"
"want to render individual glyphs at precise locations, eg, a\n"
"a glyph returned by load_char\n";
;
Py::Object
FT2Font::draw_glyph_to_bitmap(const Py::Tuple & args) {
  _VERBOSE("FT2Font::draw_glyph_to_bitmap");
  args.verify_length(4);

  if (!FT2Image::check(args[0].ptr()))
    throw Py::TypeError("Usage: draw_glyph_to_bitmap(bitmap, x,y,glyph)");
  FT2Image* im = static_cast<FT2Image*>(args[0].ptr());

  long x = Py::Int(args[1]);
  long y = Py::Int(args[2]);
  if (!Glyph::check(args[3].ptr()))
    throw Py::TypeError("Usage: draw_glyph_to_bitmap(bitmap, x,y,glyph)");
  Glyph* glyph = static_cast<Glyph*>(args[3].ptr());

  if ((size_t)glyph->glyphInd >= glyphs.size())
    throw Py::ValueError("glyph num is out of range");

  error = FT_Glyph_To_Bitmap(&glyphs[glyph->glyphInd],
                       ft_render_mode_normal,
                       0,  //no additional translation
                       1   //destroy image;
                       );
  if (error)
    throw Py::RuntimeError("Could not convert glyph to bitmap");

  FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyph->glyphInd];

  im->draw_bitmap( &bitmap->bitmap, x + bitmap->left, y);
  return Py::Object();
}

char FT2Font::get_glyph_name__doc__[] =
"get_glyph_name(index)\n"
"\n"
"Retrieves the ASCII name of a given glyph in a face.\n"
;
Py::Object
FT2Font::get_glyph_name(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_glyph_name");
  args.verify_length(1);

  if (!FT_HAS_GLYPH_NAMES(face))
    throw Py::RuntimeError("Face has no glyph names");

  char buffer[128];
  if (FT_Get_Glyph_Name(face, (FT_UInt) Py::Int(args[0]), buffer, 128))
    throw Py::RuntimeError("Could not get glyph names.");
  return Py::String(buffer);
}

char FT2Font::get_charmap__doc__[] =
"get_charmap()\n"
"\n"
"Returns a dictionary that maps the character codes of the selected charmap\n"
"(Unicode by default) to their corresponding glyph indices.\n"
;
Py::Object
FT2Font::get_charmap(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_charmap");
  args.verify_length(0);

  FT_UInt index;
  Py::Dict charmap;

  FT_ULong code = FT_Get_First_Char(face, &index);
  while (index != 0) {
    charmap[Py::Long((long) code)] = Py::Int((int) index);
    code = FT_Get_Next_Char(face, code, &index);
  }
  return charmap;
}


// ID        Platform       Encoding
// 0         Unicode        Reserved (set to 0)
// 1         Macintoch      The Script Manager code
// 2         ISO            ISO encoding
// 3         Microsoft      Microsoft encoding
// 240-255   User-defined   Reserved for all nonregistered platforms

// Code      ISO encoding scheme
// 0         7-bit ASCII
// 1         ISO 10646
// 2         ISO 8859-1

// Code      Language       Code      Language       Code
// 0         English        10        Hebrew         20        Urdu
// 1         French         11        Japanese       21        Hindi
// 2         German         12        Arabic         22        Thai
// 3         Italian        13        Finnish
// 4         Dutch          14        Greek
// 5         Swedish        15        Icelandic
// 6         Spanish        16        Maltese
// 7         Danish         17        Turkish
// 8         Portuguese     18        Yugoslavian
// 9         Norwegian      19        Chinese

// Code      Meaning        Description
// 0         Copyright notice     e.g. "Copyright Apple Computer, Inc. 1992
// 1         Font family name     e.g. "New York"
// 2         Font style           e.g. "Bold"
// 3         Font identification  e.g. "Apple Computer New York Bold Ver 1"
// 4         Full font name       e.g. "New York Bold"
// 5         Version string       e.g. "August 10, 1991, 1.08d21"
// 6         Postscript name      e.g. "Times-Bold"
// 7         Trademark
// 8         Designer             e.g. "Apple Computer"

char FT2Font::get_sfnt__doc__[] =
"get_sfnt(name)\n"
"\n"
"Get all values from the SFNT names table.  Result is a dictionary whose"
"key is the platform-ID, ISO-encoding-scheme, language-code, and"
"description.\n"
/*
  "The font name identifier codes are:\n"
  "\n"
  "  0    Copyright notice     e.g. Copyright Apple Computer, Inc. 1992\n"
  "  1    Font family name     e.g. New York\n"
  "  2    Font style           e.g. Bold\n"
  "  3    Font identification  e.g. Apple Computer New York Bold Ver 1\n"
  "  4    Full font name       e.g. New York Bold\n"
  "  5    Version string       e.g. August 10, 1991, 1.08d21\n"
  "  6    Postscript name      e.g. Times-Bold\n"
  "  7    Trademark            \n"
  "  8    Designer             e.g. Apple Computer\n"
  "  11   URL                  e.g. http://www.apple.com\n"
  "  13   Copyright license    \n"
*/
;
Py::Object
FT2Font::get_sfnt(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_sfnt");
  args.verify_length(0);

  if (!(face->face_flags & FT_FACE_FLAG_SFNT))
    throw Py::RuntimeError("No SFNT name table");

  size_t count = FT_Get_Sfnt_Name_Count(face);

  Py::Dict names;
  for (size_t j = 0; j < count; j++) {
    FT_SfntName sfnt;
    FT_Error error = FT_Get_Sfnt_Name(face, j, &sfnt);

    if (error)
      throw Py::RuntimeError("Could not get SFNT name");

    Py::Tuple key(4);
    key[0] = Py::Int(sfnt.platform_id);
    key[1] = Py::Int(sfnt.encoding_id);
    key[2] = Py::Int(sfnt.language_id);
    key[3] = Py::Int(sfnt.name_id);
    names[key] = Py::String((char *) sfnt.string,
                      (int) sfnt.string_len);
  }
  return names;
}

char FT2Font::get_name_index__doc__[] =
"get_name_index(name)\n"
"\n"
"Returns the glyph index of a given glyph name.\n"
"The glyph index 0 means `undefined character code'.\n"
;
Py::Object
FT2Font::get_name_index(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_name_index");
  args.verify_length(1);
  std::string glyphname = Py::String(args[0]);

  return Py::Long((long)
              FT_Get_Name_Index(face, (FT_String *) glyphname.c_str()));
}

char FT2Font::get_ps_font_info__doc__[] =
"get_ps_font_info()\n"
"\n"
"Return the information in the PS Font Info structure.\n"
;
Py::Object
FT2Font::get_ps_font_info(const Py::Tuple & args)
{
  _VERBOSE("FT2Font::get_ps_font_info");
  args.verify_length(0);
  PS_FontInfoRec fontinfo;

  FT_Error error = FT_Get_PS_Font_Info(face, &fontinfo);
  if (error) {
    Py::RuntimeError("Could not get PS font info");
    return Py::Object();
  }

  Py::Tuple info(9);
  info[0] = Py::String(fontinfo.version ? fontinfo.version : "");
  info[1] = Py::String(fontinfo.notice ? fontinfo.notice : "");
  info[2] = Py::String(fontinfo.full_name ? fontinfo.full_name : "");
  info[3] = Py::String(fontinfo.family_name ? fontinfo.family_name : "");
  info[4] = Py::String(fontinfo.weight ? fontinfo.weight : "");
  info[5] = Py::Long(fontinfo.italic_angle);
  info[6] = Py::Int(fontinfo.is_fixed_pitch);
  info[7] = Py::Int(fontinfo.underline_position);
  info[8] = Py::Int(fontinfo.underline_thickness);
  return info;
}

char FT2Font::get_sfnt_table__doc__[] =
"get_sfnt_table(name)\n"
"\n"
"Return one of the following SFNT tables: head, maxp, OS/2, hhea, "
"vhea, post, or pclt.\n"
;
Py::Object
FT2Font::get_sfnt_table(const Py::Tuple & args) {
  _VERBOSE("FT2Font::get_sfnt_table");
  args.verify_length(1);
  std::string tagname = Py::String(args[0]);

  int tag;
  const char *tags[] = {"head", "maxp", "OS/2", "hhea",
                  "vhea", "post", "pclt",  NULL};

  for (tag=0; tags[tag] != NULL; tag++)
    if (strcmp(tagname.c_str(), tags[tag]) == 0)
      break;

  void *table = FT_Get_Sfnt_Table(face, (FT_Sfnt_Tag) tag);
  if (!table)
    return Py::Object();
  //throw Py::RuntimeError("Could not get SFNT table");

  switch (tag) {
  case 0:
    {
      char head_dict[] = "{s:(h,h), s:(h,h), s:l, s:l, s:i, s:i,"
      "s:(l,l), s:(l,l), s:h, s:h, s:h, s:h, s:i, s:i, s:h, s:h, s:h}";
      TT_Header *t = (TT_Header *)table;
      return Py::asObject(Py_BuildValue(head_dict,
                              "version",
                              FIXED_MAJOR(t->Table_Version),
                              FIXED_MINOR(t->Table_Version),
                              "fontRevision",
                              FIXED_MAJOR(t->Font_Revision),
                              FIXED_MINOR(t->Font_Revision),
                              "checkSumAdjustment", t->CheckSum_Adjust,
                              "magicNumber" ,       t->Magic_Number,
                              "flags",         (unsigned)t->Flags,
                              "unitsPerEm",    (unsigned)t->Units_Per_EM,
                              "created",            t->Created[0], t->Created[1],
                              "modified",           t->Modified[0],t->Modified[1],
                              "xMin",               t->xMin,
                              "yMin",               t->yMin,
                              "xMax",               t->xMax,
                              "yMax",               t->yMax,
                              "macStyle",      (unsigned)t->Mac_Style,
                              "lowestRecPPEM", (unsigned)t->Lowest_Rec_PPEM,
                              "fontDirectionHint",  t->Font_Direction,
                              "indexToLocFormat",   t->Index_To_Loc_Format,
                              "glyphDataFormat",    t->Glyph_Data_Format));
    }
  case 1:
    {
      char maxp_dict[] = "{s:(h,h), s:i, s:i, s:i, s:i, s:i, s:i,"
      "s:i, s:i, s:i, s:i, s:i, s:i, s:i, s:i}";
      TT_MaxProfile *t = (TT_MaxProfile *)table;
      return Py::asObject(Py_BuildValue(maxp_dict,
                              "version",
                              FIXED_MAJOR(t->version),
                              FIXED_MINOR(t->version),
                              "numGlyphs",     (unsigned)t->numGlyphs,
                              "maxPoints",     (unsigned)t->maxPoints,
                              "maxContours",   (unsigned)t->maxContours,
                              "maxComponentPoints",
                              (unsigned)t->maxCompositePoints,
                              "maxComponentContours",
                              (unsigned)t->maxCompositeContours,
                              "maxZones",      (unsigned)t->maxZones,
                              "maxTwilightPoints",(unsigned)t->maxTwilightPoints,
                              "maxStorage",    (unsigned)t->maxStorage,
                              "maxFunctionDefs",(unsigned)t->maxFunctionDefs,
                              "maxInstructionDefs",
                              (unsigned)t->maxInstructionDefs,
                              "maxStackElements",(unsigned)t->maxStackElements,
                              "maxSizeOfInstructions",
                              (unsigned)t->maxSizeOfInstructions,
                              "maxComponentElements",
                              (unsigned)t->maxComponentElements,
                              "maxComponentDepth",
                              (unsigned)t->maxComponentDepth));
    }
  case 2:
    {
      char os_2_dict[] = "{s:h, s:h, s:h, s:h, s:h, s:h, s:h, s:h,"
      "s:h, s:h, s:h, s:h, s:h, s:h, s:h, s:h, s:s#, s:(llll),"
      "s:s#, s:h, s:h, s:h}";
      TT_OS2 *t = (TT_OS2 *)table;
      return Py::asObject(Py_BuildValue(os_2_dict,
                              "version",       (unsigned)t->version,
                              "xAvgCharWidth",      t->xAvgCharWidth,
                              "usWeightClass", (unsigned)t->usWeightClass,
                              "usWidthClass",  (unsigned)t->usWidthClass,
                              "fsType",             t->fsType,
                              "ySubscriptXSize",    t->ySubscriptXSize,
                              "ySubscriptYSize",    t->ySubscriptYSize,
                              "ySubscriptXOffset",  t->ySubscriptXOffset,
                              "ySubscriptYOffset",  t->ySubscriptYOffset,
                              "ySuperscriptXSize",  t->ySuperscriptXSize,
                              "ySuperscriptYSize",  t->ySuperscriptYSize,
                              "ySuperscriptXOffset", t->ySuperscriptXOffset,
                              "ySuperscriptYOffset", t->ySuperscriptYOffset,
                              "yStrikeoutSize",     t->yStrikeoutSize,
                              "yStrikeoutPosition", t->yStrikeoutPosition,
                              "sFamilyClass",       t->sFamilyClass,
                              "panose",             t->panose, 10,
                              "ulCharRange",
                              (unsigned long) t->ulUnicodeRange1,
                              (unsigned long) t->ulUnicodeRange2,
                              (unsigned long) t->ulUnicodeRange3,
                              (unsigned long) t->ulUnicodeRange4,
                              "achVendID",          t->achVendID, 4,
                              "fsSelection",   (unsigned)t->fsSelection,
                              "fsFirstCharIndex",(unsigned)t->usFirstCharIndex,
                              "fsLastCharIndex",(unsigned)t->usLastCharIndex));
    }
  case 3:
    {
      char hhea_dict[] = "{s:(h,h), s:h, s:h, s:h, s:i, s:h, s:h, s:h,"
      "s:h, s:h, s:h, s:h, s:i}";
      TT_HoriHeader *t = (TT_HoriHeader *)table;
      return Py::asObject(Py_BuildValue(hhea_dict,
                              "version",
                              FIXED_MAJOR(t->Version),
                              FIXED_MINOR(t->Version),
                              "ascent",             t->Ascender,
                              "descent",            t->Descender,
                              "lineGap",            t->Line_Gap,
                              "advanceWidthMax",(unsigned)t->advance_Width_Max,
                              "minLeftBearing",     t->min_Left_Side_Bearing,
                              "minRightBearing",    t->min_Right_Side_Bearing,
                              "xMaxExtent",         t->xMax_Extent,
                              "caretSlopeRise",     t->caret_Slope_Rise,
                              "caretSlopeRun",      t->caret_Slope_Run,
                              "caretOffset",        t->caret_Offset,
                              "metricDataFormat",   t->metric_Data_Format,
                              "numOfLongHorMetrics",
                              (unsigned)t->number_Of_HMetrics));
    }
  case 4:
    {
      char vhea_dict[] = "{s:(h,h), s:h, s:h, s:h, s:i, s:h, s:h, s:h,"
      "s:h, s:h, s:h, s:h, s:i}";
      TT_VertHeader *t = (TT_VertHeader *)table;
      return Py::asObject(Py_BuildValue(vhea_dict,
                              "version",
                              FIXED_MAJOR(t->Version),
                              FIXED_MINOR(t->Version),
                              "vertTypoAscender",   t->Ascender,
                              "vertTypoDescender",  t->Descender,
                              "vertTypoLineGap",    t->Line_Gap,
                              "advanceHeightMax",(unsigned)t->advance_Height_Max,
                              "minTopSideBearing",  t->min_Top_Side_Bearing,
                              "minBottomSizeBearing", t->min_Bottom_Side_Bearing,
                              "yMaxExtent",         t->yMax_Extent,
                              "caretSlopeRise",     t->caret_Slope_Rise,
                              "caretSlopeRun",      t->caret_Slope_Run,
                              "caretOffset",        t->caret_Offset,
                              "metricDataFormat",   t->metric_Data_Format,
                              "numOfLongVerMetrics",
                              (unsigned)t->number_Of_VMetrics));
    }
  case 5:
    {
      TT_Postscript *t = (TT_Postscript *)table;
      Py::Dict post;
      Py::Tuple format(2), angle(2);
      format[0] = Py::Int(FIXED_MAJOR(t->FormatType));
      format[1] = Py::Int(FIXED_MINOR(t->FormatType));
      post["format"]             = format;
      angle[0]  = Py::Int(FIXED_MAJOR(t->italicAngle));
      angle[1]  = Py::Int(FIXED_MINOR(t->italicAngle));
      post["italicAngle"]        = angle;
      post["underlinePosition"]  = Py::Int(t->underlinePosition);
      post["underlineThickness"] = Py::Int(t->underlineThickness);
      post["isFixedPitch"]       = Py::Long((long) t->isFixedPitch);
      post["minMemType42"]       = Py::Long((long) t->minMemType42);
      post["maxMemType42"]       = Py::Long((long) t->maxMemType42);
      post["minMemType1"]        = Py::Long((long) t->minMemType1);
      post["maxMemType1"]        = Py::Long((long) t->maxMemType1);
      return post;
    }
  case 6:
    {
      TT_PCLT *t = (TT_PCLT *)table;
      Py::Dict pclt;
      Py::Tuple version(2);
      version[0] = Py::Int(FIXED_MAJOR(t->Version));
      version[1] = Py::Int(FIXED_MINOR(t->Version));
      pclt["version"]            = version;
      pclt["fontNumber"]         = Py::Long((long) t->FontNumber);
      pclt["pitch"]              = Py::Int((short) t->Pitch);
      pclt["xHeight"]            = Py::Int((short) t->xHeight);
      pclt["style"]              = Py::Int((short) t->Style);
      pclt["typeFamily"]         = Py::Int((short) t->TypeFamily);
      pclt["capHeight"]          = Py::Int((short) t->CapHeight);
      pclt["symbolSet"]          = Py::Int((short) t->SymbolSet);
      pclt["typeFace"]           = Py::String((char *) t->TypeFace, 16);
      pclt["characterComplement"] = Py::String((char *)
                                     t->CharacterComplement, 8);
      pclt["filename"]           = Py::String((char *) t->FileName, 6);
      pclt["strokeWeight"]       = Py::Int((int) t->StrokeWeight);
      pclt["widthType"]          = Py::Int((int) t->WidthType);
      pclt["serifStyle"]         = Py::Int((int) t->SerifStyle);
      return pclt;
    }
  default:
    return Py::Object();
  }
}

char FT2Font::get_image__doc__ [] = 
  "get_image()\n"
  "\n"
  "Returns the underlying image buffer for this font object.\n";
Py::Object
FT2Font::get_image (const Py::Tuple &args) {
  args.verify_length(0);
  if (image) {
    Py_XINCREF(image);
    return Py::asObject(image);
  } 
  throw Py::RuntimeError("You must call .set_text() before .get_image()");
}

char FT2Font::attach_file__doc__ [] =
  "attach_file(filename)\n"
  "\n"
  "Attach a file with extra information on the font\n"
  "(in practice, an AFM file with the metrics of a Type 1 font).\n"
  "Throws an exception if unsuccessful.\n";
Py::Object
FT2Font::attach_file (const Py::Tuple &args) {
  args.verify_length(1);

  std::string filename = Py::String(args[0]);
  FT_Error error = 
    FT_Attach_File(face, filename.c_str());

  if (error) {
    std::ostringstream s;
    s << "Could not attach file " << filename
      << " (freetype error code " << error << ")" << std::endl;
    throw Py::RuntimeError(s.str());
  }
  return Py::Object();
}

Py::Object
ft2font_module::new_ft2image (const Py::Tuple &args) {
  args.verify_length(2);

  int width = Py::Int(args[0]);
  int height = Py::Int(args[1]);

  return Py::asObject( new FT2Image(width, height) );
}

Py::Object
ft2font_module::new_ft2font (const Py::Tuple &args) {
  _VERBOSE("ft2font_module::new_ft2font ");
  args.verify_length(1);

  std::string facefile = Py::String(args[0]);
  return Py::asObject( new FT2Font(facefile) );
}

void
FT2Image::init_type() {
 _VERBOSE("FT2Image::init_type");
 behaviors().name("FT2Image");
 behaviors().doc("FT2Image");

 add_varargs_method("write_bitmap", &FT2Image::py_write_bitmap,
                FT2Image::write_bitmap__doc__);
 add_varargs_method("draw_rect", &FT2Image::py_draw_rect,
                FT2Image::draw_rect__doc__);
 add_varargs_method("draw_rect_filled", &FT2Image::py_draw_rect_filled,
                FT2Image::draw_rect_filled__doc__);
 add_varargs_method("as_str", &FT2Image::py_as_str,
                FT2Image::as_str__doc__);
 add_varargs_method("as_rgb_str", &FT2Image::py_as_rgb_str,
                FT2Image::as_rgb_str__doc__);
 add_varargs_method("as_rgba_str", &FT2Image::py_as_rgba_str,
                FT2Image::as_rgba_str__doc__);
 add_varargs_method("get_width", &FT2Image::py_get_width,
                "Returns the width of the image");
 add_varargs_method("get_height", &FT2Image::py_get_height,
                "Returns the height of the image");
}

void
Glyph::init_type() {
  _VERBOSE("Glyph::init_type");
  behaviors().name("Glyph");
  behaviors().doc("Glyph");
  behaviors().supportGetattr();
  behaviors().supportSetattr();
}

void
FT2Font::init_type() {
  _VERBOSE("FT2Font::init_type");
  behaviors().name("FT2Font");
  behaviors().doc("FT2Font");

  add_varargs_method("clear", &FT2Font::clear,
                 FT2Font::clear__doc__);
  add_varargs_method("draw_glyph_to_bitmap", &FT2Font::draw_glyph_to_bitmap,
                 FT2Font::draw_glyph_to_bitmap__doc__);
  add_varargs_method("draw_glyphs_to_bitmap", &FT2Font::draw_glyphs_to_bitmap,
                 FT2Font::draw_glyphs_to_bitmap__doc__);
  add_varargs_method("get_xys", &FT2Font::get_xys,
                 FT2Font::get_xys__doc__);

  add_varargs_method("get_glyph", &FT2Font::get_glyph,
                 FT2Font::get_glyph__doc__);
  add_varargs_method("get_num_glyphs", &FT2Font::get_num_glyphs,
                 FT2Font::get_num_glyphs__doc__);
  add_keyword_method("load_char", &FT2Font::load_char,
                 FT2Font::load_char__doc__);
  add_keyword_method("set_text", &FT2Font::set_text,
                 FT2Font::set_text__doc__);
  add_varargs_method("set_size", &FT2Font::set_size,
                 FT2Font::set_size__doc__);
  add_varargs_method("set_charmap", &FT2Font::set_charmap,
                 FT2Font::set_charmap__doc__);

  add_varargs_method("get_width_height", &FT2Font::get_width_height,
                 FT2Font::get_width_height__doc__);
  add_varargs_method("get_descent", &FT2Font::get_descent,
                 FT2Font::get_descent__doc__);
  add_varargs_method("get_glyph_name", &FT2Font::get_glyph_name,
                 FT2Font::get_glyph_name__doc__);
  add_varargs_method("get_charmap", &FT2Font::get_charmap,
                 FT2Font::get_charmap__doc__);
  add_varargs_method("get_kerning", &FT2Font::get_kerning,
                 FT2Font::get_kerning__doc__);
  add_varargs_method("get_sfnt", &FT2Font::get_sfnt,
                 FT2Font::get_sfnt__doc__);
  add_varargs_method("get_name_index", &FT2Font::get_name_index,
                 FT2Font::get_name_index__doc__);
  add_varargs_method("get_ps_font_info", &FT2Font::get_ps_font_info,
                 FT2Font::get_ps_font_info__doc__);
  add_varargs_method("get_sfnt_table", &FT2Font::get_sfnt_table,
                 FT2Font::get_sfnt_table__doc__);
  add_varargs_method("get_image", &FT2Font::get_image,
                 FT2Font::get_image__doc__);
  add_varargs_method("attach_file", &FT2Font::attach_file,
                     FT2Font::attach_file__doc__);

  behaviors().supportGetattr();
  behaviors().supportSetattr();
};

//todo add module docs strings

char ft2font__doc__[] =
"ft2font\n"
"\n"
"Methods:\n"
"  FT2Font(ttffile)\n"
"Face Constants\n"
"  SCALABLE               scalable\n"
"  FIXED_SIZES            \n"
"  FIXED_WIDTH            \n"
"  SFNT                   \n"
"  HORIZONTAL             \n"
"  VERTICAL               \n"
"  KERNING                \n"
"  FAST_GLYPHS            \n"
"  MULTIPLE_MASTERS       \n"
"  GLYPH_NAMES            \n"
"  EXTERNAL_STREAM        \n"
"Style Constants\n"
"  ITALIC                 \n"
"  BOLD                   \n"
;

/* Function of no arguments returning new FT2Font object */
char ft2font_new__doc__[] =
"FT2Font(ttffile)\n"
"\n"
"Create a new FT2Font object\n"
"The following global font attributes are defined:\n"
"  num_faces              number of faces in file\n"
"  face_flags             face flags  (int type); see the ft2font constants\n"
"  style_flags            style flags  (int type); see the ft2font constants\n"
"  num_glyphs             number of glyphs in the face\n"
"  family_name            face family name\n"
"  style_name             face syle name\n"
"  num_fixed_sizes        number of bitmap in the face\n"
"  scalable               face is scalable\n"
"\n"
"The following are available, if scalable is true:\n"
"  bbox                   face global bounding box (xmin, ymin, xmax, ymax)\n"
"  units_per_EM           number of font units covered by the EM\n"
"  ascender               ascender in 26.6 units\n"
"  descender              descender in 26.6 units\n"
"  height                 height in 26.6 units; used to compute a default\n"
"                         line spacing (baseline-to-baseline distance)\n"
"  max_advance_width      maximum horizontal cursor advance for all glyphs\n"
"  max_advance_height     same for vertical layout\n"
"  underline_position     vertical position of the underline bar\n"
"  underline_thickness    vertical thickness of the underline\n"
"  postscript_name        PostScript name of the font\n"
;

#if defined(_MSC_VER)
DL_EXPORT(void)
#elif defined(__cplusplus)
  extern "C" void
#else
void
#endif
initft2font(void)
{
  static ft2font_module* ft2font = new ft2font_module;

  Py::Dict d = ft2font->moduleDictionary();
  d["SCALABLE"]   = Py::Int(FT_FACE_FLAG_SCALABLE);
  d["FIXED_SIZES"]      = Py::Int(FT_FACE_FLAG_FIXED_SIZES);
  d["FIXED_WIDTH"]      = Py::Int(FT_FACE_FLAG_FIXED_WIDTH);
  d["SFNT"]             = Py::Int(FT_FACE_FLAG_SFNT);
  d["HORIZONTAL"]       = Py::Int(FT_FACE_FLAG_HORIZONTAL);
  d["VERTICAL"]   = Py::Int(FT_FACE_FLAG_SCALABLE);
  d["KERNING"]          = Py::Int(FT_FACE_FLAG_KERNING);
  d["FAST_GLYPHS"]      = Py::Int(FT_FACE_FLAG_FAST_GLYPHS);
  d["MULTIPLE_MASTERS"] = Py::Int(FT_FACE_FLAG_MULTIPLE_MASTERS);
  d["GLYPH_NAMES"]      = Py::Int(FT_FACE_FLAG_GLYPH_NAMES);
  d["EXTERNAL_STREAM"]  = Py::Int(FT_FACE_FLAG_EXTERNAL_STREAM);
  d["ITALIC"]           = Py::Int(FT_STYLE_FLAG_ITALIC);
  d["BOLD"]             = Py::Int(FT_STYLE_FLAG_BOLD);
  d["KERNING_DEFAULT"]  = Py::Int(FT_KERNING_DEFAULT);
  d["KERNING_UNFITTED"]  = Py::Int(FT_KERNING_UNFITTED);
  d["KERNING_UNSCALED"]  = Py::Int(FT_KERNING_UNSCALED);
  
  d["LOAD_DEFAULT"]          = Py::Long(FT_LOAD_DEFAULT);
  d["LOAD_NO_SCALE"]         = Py::Long(FT_LOAD_NO_SCALE);
  d["LOAD_NO_HINTING"]       = Py::Long(FT_LOAD_NO_HINTING);
  d["LOAD_RENDER"]           = Py::Long(FT_LOAD_RENDER);
  d["LOAD_NO_BITMAP"]        = Py::Long(FT_LOAD_NO_BITMAP);
  d["LOAD_VERTICAL_LAYOUT"]  = Py::Long(FT_LOAD_VERTICAL_LAYOUT);
  d["LOAD_FORCE_AUTOHINT"]   = Py::Long(FT_LOAD_FORCE_AUTOHINT);
  d["LOAD_CROP_BITMAP"]      = Py::Long(FT_LOAD_CROP_BITMAP);
  d["LOAD_PEDANTIC"]         = Py::Long(FT_LOAD_PEDANTIC);
  d["LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH"] = 
    Py::Long(FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH);
  d["LOAD_NO_RECURSE"]       = Py::Long(FT_LOAD_NO_RECURSE);
  d["LOAD_IGNORE_TRANSFORM"] = Py::Long(FT_LOAD_IGNORE_TRANSFORM);
  d["LOAD_MONOCHROME"]       = Py::Long(FT_LOAD_MONOCHROME);
  d["LOAD_LINEAR_DESIGN"]    = Py::Long(FT_LOAD_LINEAR_DESIGN);
  // These need casting because large-valued numeric literals could
  // be either longs or unsigned longs:
  d["LOAD_NO_AUTOHINT"]      = Py::Long((unsigned long)FT_LOAD_NO_AUTOHINT);
  d["LOAD_TARGET_NORMAL"]    = Py::Long((unsigned long)FT_LOAD_TARGET_NORMAL);
  d["LOAD_TARGET_LIGHT"]     = Py::Long((unsigned long)FT_LOAD_TARGET_LIGHT);
  d["LOAD_TARGET_MONO"]      = Py::Long((unsigned long)FT_LOAD_TARGET_MONO);
  d["LOAD_TARGET_LCD"]       = Py::Long((unsigned long)FT_LOAD_TARGET_LCD);
  d["LOAD_TARGET_LCD_V"]     = Py::Long((unsigned long)FT_LOAD_TARGET_LCD_V);

  //initialize library
  int error = FT_Init_FreeType( &_ft2Library );

  if (error)
    throw Py::RuntimeError("Could not find initialize the freetype2 library");
}

ft2font_module::~ft2font_module() {
  FT_Done_FreeType( _ft2Library );
}

Generated by  Doxygen 1.6.0   Back to index