[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/dpsanders/matplotlib/transforms/src/ft2font.cpp [Back]  [Original]

#include 
#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_height )
    throw Py::ValueError("Rect coords outside image bounds");

  size_t top = y0*_width;
  size_t bottom = y1*_width;
  for (size_t i=x0; iresize(_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 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 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 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 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= 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 

Web Proxy Viewer  |  New URL  |  Original Page