/* -*- mode: c++; c-basic-offset: 4 -*- */
#include "ft2font.h"
#include "mplutils.h"
#include
#include "numpy/arrayobject.h"
/*
By definition, FT_FIXED as 2 16bit values stored in a single long.
We cast to long to ensure the correct Py::Int convertor is called
*/
#define FIXED_MAJOR(val) (long) ((val & 0xffff000) >> 16)
#define FIXED_MINOR(val) (long) (val & 0xffff)
/**
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(long width, long height)
{
if (width < 0)
{
width = 1;
}
if (height < 0)
{
height = 1;
}
size_t numBytes = width * height;
if ((unsigned long)width != _width || (unsigned long)height != _height)
{
if (numBytes > _width*_height)
{
delete [] _buffer;
_buffer = NULL;
_buffer = new unsigned char [numBytes];
}
_width = (unsigned long)width;
_height = (unsigned long)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 > _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 = std::min(x0, _width);
y0 = std::min(y0, _height);
x1 = std::min(x1, _width);
y1 = std::min(y1, _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)
);
}
char FT2Image::as_array__doc__[] =
"x = image.as_array()\n"
"\n"
"Return the image buffer as a width x height numpy array of ubyte \n"
"\n"
;
Py::Object
FT2Image::py_as_array(const Py::Tuple & args)
{
_VERBOSE("FT2Image::as_array");
args.verify_length(0);
npy_intp dimensions[2];
dimensions[0] = get_height(); //numrows
dimensions[1] = get_width(); //numcols
PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNewFromData(2, dimensions, PyArray_UBYTE, _buffer);
return Py::asObject((PyObject*)A);
}
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);
}
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
FT2Font::get_path()
{
//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
if (!face->glyph) {
throw Py::ValueError("No glyph loaded");
}
enum {STOP = 0,
MOVETO = 1,
LINETO = 2,
CURVE3 = 3,
CURVE4 = 4,
ENDPOLY = 0x4f};
FT_Outline& outline = face->glyph->outline;
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
int count;
count = 0;
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)
{
throw Py::RuntimeError("A contour cannot start with a cubic control point");
}
count++;
while (point < limit)
{
point++;
tags++;
tag = FT_CURVE_TAG(tags[0]);
switch (tag)
{
case FT_CURVE_TAG_ON: // emit a single line_to
{
count++;
continue;
}
case FT_CURVE_TAG_CONIC: // consume conic arcs
{
Count_Do_Conic:
if (point < limit)
{
point++;
tags++;
tag = FT_CURVE_TAG(tags[0]);
if (tag == FT_CURVE_TAG_ON)
{
count += 2;
continue;
}
if (tag != FT_CURVE_TAG_CONIC)
{
throw Py::RuntimeError("Invalid font");
}
count += 2;
goto Count_Do_Conic;
}
count += 2;
goto Count_Close;
}
default: // FT_CURVE_TAG_CUBIC
{
if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
{
throw Py::RuntimeError("Invalid font");
}
point += 2;
tags += 2;
if (point x);
double y = flip_y ? -conv(point->y) : conv(point->y);
*(outpoints++) = x;
*(outpoints++) = y;
*(outcodes++) = LINETO;
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);
*(outpoints++) = xctl;
*(outpoints++) = yctl;
*(outpoints++) = xto;
*(outpoints++) = yto;
*(outcodes++) = CURVE3;
*(outcodes++) = CURVE3;
continue;
}
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);
*(outpoints++) = xctl;
*(outpoints++) = yctl;
*(outpoints++) = xto;
*(outpoints++) = yto;
*(outcodes++) = CURVE3;
*(outcodes++) = CURVE3;
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);
*(outpoints++) = xctl;
*(outpoints++) = yctl;
*(outpoints++) = xto;
*(outpoints++) = yto;
*(outcodes++) = CURVE3;
*(outcodes++) = CURVE3;
goto Close;
}
default: // FT_CURVE_TAG_CUBIC
{
FT_Vector vec1, vec2;
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);
(*outpoints++) = xctl1;
(*outpoints++) = yctl1;
(*outpoints++) = xctl2;
(*outpoints++) = yctl2;
(*outpoints++) = xto;
(*outpoints++) = yto;
(*outcodes++) = CURVE4;
(*outcodes++) = CURVE4;
(*outcodes++) = CURVE4;
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);
(*outpoints++) = xctl1;
(*outpoints++) = yctl1;
(*outpoints++) = xctl2;
(*outpoints++) = yctl2;
(*outpoints++) = xto;
(*outpoints++) = yto;
(*outcodes++) = CURVE4;
(*outcodes++) = CURVE4;
(*outcodes++) = CURVE4;
goto Close;
}
}
}
(*outpoints++) = 0.0;
(*outpoints++) = 0.0;
(*outcodes++) = ENDPOLY;
Close:
first = last + 1;
}
if (outcodes - (unsigned char *)PyArray_DATA(codes) != count) {
throw Py::RuntimeError("Font path size doesn't match");
}
return result;
}
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 < glyphs.size(); i++)
{
FT_Done_Glyph(glyphs[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]);
}
glyphs.clear();
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();
}
char FT2Font::select_charmap__doc__[] =
"select_charmap(i)\n"
"\n"
"select charmap i where i is one of the FT_Encoding number\n"
;
Py::Object
FT2Font::select_charmap(const Py::Tuple & args)
{
_VERBOSE("FT2Font::set_charmap");
args.verify_length(1);
unsigned long i = Py::Long(args[0]);
//if (FT_Select_Charmap( face, FT_ENCODING_ADOBE_CUSTOM ))
if (FT_Select_Charmap(face, (FT_Encoding) i))
{
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;
int right_side = 0;
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.xMin == glyph_bbox.xMax)
{
right_side += glyphs[n]->advance.x >> 10;
if (right_side > bbox.xMax) bbox.xMax = right_side;
}
else
{
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 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(args[0].ptr());
double xd = Py::Float(args[1]);
double yd = Py::Float(args[2]);
long x = (long)xd;
long y = (long)yd;
FT_Vector sub_offset;
sub_offset.x = 0; // int((xd - (double)x) * 64.0);
sub_offset.y = 0; // int((yd - (double)y) * 64.0);
if (!Glyph::check(args[3].ptr()))
{
throw Py::TypeError("Usage: draw_glyph_to_bitmap(bitmap, x,y,glyph)");
}
Glyph* glyph = static_cast(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,
&sub_offset, //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;
//std::cout encoding