1 /* -*- mode: c++; c-basic-offset: 4 -*- */
2 
3 #define NO_IMPORT_ARRAY
4 
5 #include <algorithm>
6 #include <sstream>
7 #include <stdexcept>
8 #include <string>
9 
10 #include "ft2font.h"
11 #include "mplutils.h"
12 #include "numpy_cpp.h"
13 #include "py_exceptions.h"
14 
15 #ifndef M_PI
16 #define M_PI 3.14159265358979323846264338328
17 #endif
18 
19 /**
20  To improve the hinting of the fonts, this code uses a hack
21  presented here:
22 
23  http://antigrain.com/research/font_rasterization/index.html
24 
25  The idea is to limit the effect of hinting in the x-direction, while
26  preserving hinting in the y-direction.  Since freetype does not
27  support this directly, the dpi in the x-direction is set higher than
28  in the y-direction, which affects the hinting grid.  Then, a global
29  transform is placed on the font to shrink it back to the desired
30  size.  While it is a bit surprising that the dpi setting affects
31  hinting, whereas the global transform does not, this is documented
32  behavior of FreeType, and therefore hopefully unlikely to change.
33  The FreeType 2 tutorial says:
34 
35       NOTE: The transformation is applied to every glyph that is
36       loaded through FT_Load_Glyph and is completely independent of
37       any hinting process. This means that you won't get the same
38       results if you load a glyph at the size of 24 pixels, or a glyph
39       at the size at 12 pixels scaled by 2 through a transform,
40       because the hints will have been computed differently (except
41       you have disabled hints).
42  */
43 
44 FT_Library _ft2Library;
45 
throw_ft_error(std::string message,FT_Error error)46 void throw_ft_error(std::string message, FT_Error error) {
47     std::ostringstream os("");
48     os << message << " (error code 0x" << std::hex << error << ")";
49     throw std::runtime_error(os.str());
50 }
51 
FT2Image()52 FT2Image::FT2Image() : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0)
53 {
54 }
55 
FT2Image(unsigned long width,unsigned long height)56 FT2Image::FT2Image(unsigned long width, unsigned long height)
57     : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0)
58 {
59     resize(width, height);
60 }
61 
~FT2Image()62 FT2Image::~FT2Image()
63 {
64     delete[] m_buffer;
65 }
66 
resize(long width,long height)67 void FT2Image::resize(long width, long height)
68 {
69     if (width <= 0) {
70         width = 1;
71     }
72     if (height <= 0) {
73         height = 1;
74     }
75     size_t numBytes = width * height;
76 
77     if ((unsigned long)width != m_width || (unsigned long)height != m_height) {
78         if (numBytes > m_width * m_height) {
79             delete[] m_buffer;
80             m_buffer = NULL;
81             m_buffer = new unsigned char[numBytes];
82         }
83 
84         m_width = (unsigned long)width;
85         m_height = (unsigned long)height;
86     }
87 
88     if (numBytes && m_buffer) {
89         memset(m_buffer, 0, numBytes);
90     }
91 
92     m_dirty = true;
93 }
94 
draw_bitmap(FT_Bitmap * bitmap,FT_Int x,FT_Int y)95 void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y)
96 {
97     FT_Int image_width = (FT_Int)m_width;
98     FT_Int image_height = (FT_Int)m_height;
99     FT_Int char_width = bitmap->width;
100     FT_Int char_height = bitmap->rows;
101 
102     FT_Int x1 = CLAMP(x, 0, image_width);
103     FT_Int y1 = CLAMP(y, 0, image_height);
104     FT_Int x2 = CLAMP(x + char_width, 0, image_width);
105     FT_Int y2 = CLAMP(y + char_height, 0, image_height);
106 
107     FT_Int x_start = MAX(0, -x);
108     FT_Int y_offset = y1 - MAX(0, -y);
109 
110     if (bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) {
111         for (FT_Int i = y1; i < y2; ++i) {
112             unsigned char *dst = m_buffer + (i * image_width + x1);
113             unsigned char *src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start);
114             for (FT_Int j = x1; j < x2; ++j, ++dst, ++src)
115                 *dst |= *src;
116         }
117     } else if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) {
118         for (FT_Int i = y1; i < y2; ++i) {
119             unsigned char *dst = m_buffer + (i * image_width + x1);
120             unsigned char *src = bitmap->buffer + ((i - y_offset) * bitmap->pitch);
121             for (FT_Int j = x1; j < x2; ++j, ++dst) {
122                 int x = (j - x1 + x_start);
123                 int val = *(src + (x >> 3)) & (1 << (7 - (x & 0x7)));
124                 *dst = val ? 255 : *dst;
125             }
126         }
127     } else {
128         throw std::runtime_error("Unknown pixel mode");
129     }
130 
131     m_dirty = true;
132 }
133 
draw_rect(unsigned long x0,unsigned long y0,unsigned long x1,unsigned long y1)134 void FT2Image::draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1)
135 {
136     if (x0 > m_width || x1 > m_width || y0 > m_height || y1 > m_height) {
137         throw std::runtime_error("Rect coords outside image bounds");
138     }
139 
140     size_t top = y0 * m_width;
141     size_t bottom = y1 * m_width;
142     for (size_t i = x0; i < x1 + 1; ++i) {
143         m_buffer[i + top] = 255;
144         m_buffer[i + bottom] = 255;
145     }
146 
147     for (size_t j = y0 + 1; j < y1; ++j) {
148         m_buffer[x0 + j * m_width] = 255;
149         m_buffer[x1 + j * m_width] = 255;
150     }
151 
152     m_dirty = true;
153 }
154 
155 void
draw_rect_filled(unsigned long x0,unsigned long y0,unsigned long x1,unsigned long y1)156 FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1)
157 {
158     x0 = std::min(x0, m_width);
159     y0 = std::min(y0, m_height);
160     x1 = std::min(x1 + 1, m_width);
161     y1 = std::min(y1 + 1, m_height);
162 
163     for (size_t j = y0; j < y1; j++) {
164         for (size_t i = x0; i < x1; i++) {
165             m_buffer[i + j * m_width] = 255;
166         }
167     }
168 
169     m_dirty = true;
170 }
171 
ft_get_char_index_or_warn(FT_Face face,FT_ULong charcode)172 static FT_UInt ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode)
173 {
174     FT_UInt glyph_index = FT_Get_Char_Index(face, charcode);
175     if (!glyph_index) {
176         PyErr_WarnFormat(NULL, 1, "Glyph %lu missing from current font.", charcode);
177         // Apparently PyErr_WarnFormat returns 0 even if the exception propagates
178         // due to running with -Werror, so check the error flag directly instead.
179         if (PyErr_Occurred()) {
180             throw py::exception();
181         }
182     }
183     return glyph_index;
184 }
185 
186 
187 // ft_outline_decomposer should be passed to FT_Outline_Decompose.  On the
188 // first pass, vertices and codes are set to NULL, and index is simply
189 // incremented for each vertex that should be inserted, so that it is set, at
190 // the end, to the total number of vertices.  On a second pass, vertices and
191 // codes should point to correctly sized arrays, and index set again to zero,
192 // to get fill vertices and codes with the outline decomposition.
193 struct ft_outline_decomposer
194 {
195     int index;
196     double* vertices;
197     unsigned char* codes;
198 };
199 
200 static int
ft_outline_move_to(FT_Vector const * to,void * user)201 ft_outline_move_to(FT_Vector const* to, void* user)
202 {
203     ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
204     if (d->codes) {
205         if (d->index) {
206             // Appending ENDPOLY is important to make patheffects work.
207             *(d->vertices++) = 0;
208             *(d->vertices++) = 0;
209             *(d->codes++) = ENDPOLY;
210         }
211         *(d->vertices++) = to->x / 64.;
212         *(d->vertices++) = to->y / 64.;
213         *(d->codes++) = MOVETO;
214     }
215     d->index += d->index ? 2 : 1;
216     return 0;
217 }
218 
219 static int
ft_outline_line_to(FT_Vector const * to,void * user)220 ft_outline_line_to(FT_Vector const* to, void* user)
221 {
222     ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
223     if (d->codes) {
224         *(d->vertices++) = to->x / 64.;
225         *(d->vertices++) = to->y / 64.;
226         *(d->codes++) = LINETO;
227     }
228     d->index++;
229     return 0;
230 }
231 
232 static int
ft_outline_conic_to(FT_Vector const * control,FT_Vector const * to,void * user)233 ft_outline_conic_to(FT_Vector const* control, FT_Vector const* to, void* user)
234 {
235     ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
236     if (d->codes) {
237         *(d->vertices++) = control->x / 64.;
238         *(d->vertices++) = control->y / 64.;
239         *(d->vertices++) = to->x / 64.;
240         *(d->vertices++) = to->y / 64.;
241         *(d->codes++) = CURVE3;
242         *(d->codes++) = CURVE3;
243     }
244     d->index += 2;
245     return 0;
246 }
247 
248 static int
ft_outline_cubic_to(FT_Vector const * c1,FT_Vector const * c2,FT_Vector const * to,void * user)249 ft_outline_cubic_to(
250   FT_Vector const* c1, FT_Vector const* c2, FT_Vector const* to, void* user)
251 {
252     ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user);
253     if (d->codes) {
254         *(d->vertices++) = c1->x / 64.;
255         *(d->vertices++) = c1->y / 64.;
256         *(d->vertices++) = c2->x / 64.;
257         *(d->vertices++) = c2->y / 64.;
258         *(d->vertices++) = to->x / 64.;
259         *(d->vertices++) = to->y / 64.;
260         *(d->codes++) = CURVE4;
261         *(d->codes++) = CURVE4;
262         *(d->codes++) = CURVE4;
263     }
264     d->index += 3;
265     return 0;
266 }
267 
268 static FT_Outline_Funcs ft_outline_funcs = {
269     ft_outline_move_to,
270     ft_outline_line_to,
271     ft_outline_conic_to,
272     ft_outline_cubic_to};
273 
274 PyObject*
get_path()275 FT2Font::get_path()
276 {
277     if (!face->glyph) {
278         PyErr_SetString(PyExc_RuntimeError, "No glyph loaded");
279         return NULL;
280     }
281     ft_outline_decomposer decomposer = {};
282     if (FT_Error error =
283         FT_Outline_Decompose(
284           &face->glyph->outline, &ft_outline_funcs, &decomposer)) {
285         PyErr_Format(PyExc_RuntimeError,
286                      "FT_Outline_Decompose failed with error 0x%x", error);
287         return NULL;
288     }
289     if (!decomposer.index) {  // Don't append ENDPOLY to null glyphs.
290       npy_intp vertices_dims[2] = { 0, 2 };
291       numpy::array_view<double, 2> vertices(vertices_dims);
292       npy_intp codes_dims[1] = { 0 };
293       numpy::array_view<unsigned char, 1> codes(codes_dims);
294       return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj());
295     }
296     npy_intp vertices_dims[2] = { decomposer.index + 1, 2 };
297     numpy::array_view<double, 2> vertices(vertices_dims);
298     npy_intp codes_dims[1] = { decomposer.index + 1 };
299     numpy::array_view<unsigned char, 1> codes(codes_dims);
300     decomposer.index = 0;
301     decomposer.vertices = vertices.data();
302     decomposer.codes = codes.data();
303     if (FT_Error error =
304         FT_Outline_Decompose(
305           &face->glyph->outline, &ft_outline_funcs, &decomposer)) {
306         PyErr_Format(PyExc_RuntimeError,
307                      "FT_Outline_Decompose failed with error 0x%x", error);
308         return NULL;
309     }
310     *(decomposer.vertices++) = 0;
311     *(decomposer.vertices++) = 0;
312     *(decomposer.codes++) = ENDPOLY;
313     return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj());
314 }
315 
FT2Font(FT_Open_Args & open_args,long hinting_factor_)316 FT2Font::FT2Font(FT_Open_Args &open_args, long hinting_factor_) : image(), face(NULL)
317 {
318     clear();
319 
320     FT_Error error = FT_Open_Face(_ft2Library, &open_args, 0, &face);
321 
322     if (error == FT_Err_Unknown_File_Format) {
323         throw std::runtime_error("Can not load face.  Unknown file format.");
324     } else if (error == FT_Err_Cannot_Open_Resource) {
325         throw std::runtime_error("Can not load face.  Can not open resource.");
326     } else if (error == FT_Err_Invalid_File_Format) {
327         throw std::runtime_error("Can not load face.  Invalid file format.");
328     } else if (error) {
329         throw_ft_error("Can not load face", error);
330     }
331 
332     // set default kerning factor to 0, i.e., no kerning manipulation
333     kerning_factor = 0;
334 
335     // set a default fontsize 12 pt at 72dpi
336     hinting_factor = hinting_factor_;
337 
338     error = FT_Set_Char_Size(face, 12 * 64, 0, 72 * (unsigned int)hinting_factor, 72);
339     if (error) {
340         FT_Done_Face(face);
341         throw_ft_error("Could not set the fontsize", error);
342     }
343 
344     if (open_args.stream != NULL) {
345         face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM;
346     }
347 
348     FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 };
349     FT_Set_Transform(face, &transform, 0);
350 }
351 
~FT2Font()352 FT2Font::~FT2Font()
353 {
354     for (size_t i = 0; i < glyphs.size(); i++) {
355         FT_Done_Glyph(glyphs[i]);
356     }
357 
358     if (face) {
359         FT_Done_Face(face);
360     }
361 }
362 
clear()363 void FT2Font::clear()
364 {
365     pen.x = 0;
366     pen.y = 0;
367 
368     for (size_t i = 0; i < glyphs.size(); i++) {
369         FT_Done_Glyph(glyphs[i]);
370     }
371 
372     glyphs.clear();
373 }
374 
set_size(double ptsize,double dpi)375 void FT2Font::set_size(double ptsize, double dpi)
376 {
377     FT_Error error = FT_Set_Char_Size(
378         face, (FT_F26Dot6)(ptsize * 64), 0, (FT_UInt)(dpi * hinting_factor), (FT_UInt)dpi);
379     if (error) {
380         throw_ft_error("Could not set the fontsize", error);
381     }
382     FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 };
383     FT_Set_Transform(face, &transform, 0);
384 }
385 
set_charmap(int i)386 void FT2Font::set_charmap(int i)
387 {
388     if (i >= face->num_charmaps) {
389         throw std::runtime_error("i exceeds the available number of char maps");
390     }
391     FT_CharMap charmap = face->charmaps[i];
392     if (FT_Error error = FT_Set_Charmap(face, charmap)) {
393         throw_ft_error("Could not set the charmap", error);
394     }
395 }
396 
select_charmap(unsigned long i)397 void FT2Font::select_charmap(unsigned long i)
398 {
399     if (FT_Error error = FT_Select_Charmap(face, (FT_Encoding)i)) {
400         throw_ft_error("Could not set the charmap", error);
401     }
402 }
403 
get_kerning(FT_UInt left,FT_UInt right,FT_UInt mode)404 int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode)
405 {
406     if (!FT_HAS_KERNING(face)) {
407         return 0;
408     }
409     FT_Vector delta;
410 
411     if (!FT_Get_Kerning(face, left, right, mode, &delta)) {
412         return (int)(delta.x) / (hinting_factor << kerning_factor);
413     } else {
414         return 0;
415     }
416 }
417 
set_kerning_factor(int factor)418 void FT2Font::set_kerning_factor(int factor)
419 {
420     kerning_factor = factor;
421 }
422 
set_text(size_t N,uint32_t * codepoints,double angle,FT_Int32 flags,std::vector<double> & xys)423 void FT2Font::set_text(
424     size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector<double> &xys)
425 {
426     FT_Matrix matrix; /* transformation matrix */
427 
428     angle = angle / 360.0 * 2 * M_PI;
429 
430     // this computes width and height in subpixels so we have to divide by 64
431     matrix.xx = (FT_Fixed)(cos(angle) * 0x10000L);
432     matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L);
433     matrix.yx = (FT_Fixed)(sin(angle) * 0x10000L);
434     matrix.yy = (FT_Fixed)(cos(angle) * 0x10000L);
435 
436     FT_Bool use_kerning = FT_HAS_KERNING(face);
437     FT_UInt previous = 0;
438 
439     clear();
440 
441     bbox.xMin = bbox.yMin = 32000;
442     bbox.xMax = bbox.yMax = -32000;
443 
444     for (unsigned int n = 0; n < N; n++) {
445         FT_UInt glyph_index;
446         FT_BBox glyph_bbox;
447         FT_Pos last_advance;
448 
449         glyph_index = ft_get_char_index_or_warn(face, codepoints[n]);
450 
451         // retrieve kerning distance and move pen position
452         if (use_kerning && previous && glyph_index) {
453             FT_Vector delta;
454             FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
455             pen.x += delta.x / (hinting_factor << kerning_factor);
456         }
457         if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) {
458             throw_ft_error("Could not load glyph", error);
459         }
460         // ignore errors, jump to next glyph
461 
462         // extract glyph image and store it in our table
463 
464         FT_Glyph thisGlyph;
465         if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) {
466             throw_ft_error("Could not get glyph", error);
467         }
468         // ignore errors, jump to next glyph
469 
470         last_advance = face->glyph->advance.x;
471         FT_Glyph_Transform(thisGlyph, 0, &pen);
472         FT_Glyph_Transform(thisGlyph, &matrix, 0);
473         xys.push_back(pen.x);
474         xys.push_back(pen.y);
475 
476         FT_Glyph_Get_CBox(thisGlyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph_bbox);
477 
478         bbox.xMin = std::min(bbox.xMin, glyph_bbox.xMin);
479         bbox.xMax = std::max(bbox.xMax, glyph_bbox.xMax);
480         bbox.yMin = std::min(bbox.yMin, glyph_bbox.yMin);
481         bbox.yMax = std::max(bbox.yMax, glyph_bbox.yMax);
482 
483         pen.x += last_advance;
484 
485         previous = glyph_index;
486         glyphs.push_back(thisGlyph);
487     }
488 
489     FT_Vector_Transform(&pen, &matrix);
490     advance = pen.x;
491 
492     if (bbox.xMin > bbox.xMax) {
493         bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0;
494     }
495 }
496 
load_char(long charcode,FT_Int32 flags)497 void FT2Font::load_char(long charcode, FT_Int32 flags)
498 {
499     FT_UInt glyph_index = ft_get_char_index_or_warn(face, (FT_ULong)charcode);
500     if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) {
501         throw_ft_error("Could not load charcode", error);
502     }
503     FT_Glyph thisGlyph;
504     if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) {
505         throw_ft_error("Could not get glyph", error);
506     }
507     glyphs.push_back(thisGlyph);
508 }
509 
load_glyph(FT_UInt glyph_index,FT_Int32 flags)510 void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags)
511 {
512     if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) {
513         throw_ft_error("Could not load glyph", error);
514     }
515     FT_Glyph thisGlyph;
516     if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) {
517         throw_ft_error("Could not get glyph", error);
518     }
519     glyphs.push_back(thisGlyph);
520 }
521 
get_width_height(long * width,long * height)522 void FT2Font::get_width_height(long *width, long *height)
523 {
524     *width = advance;
525     *height = bbox.yMax - bbox.yMin;
526 }
527 
get_descent()528 long FT2Font::get_descent()
529 {
530     return -bbox.yMin;
531 }
532 
get_bitmap_offset(long * x,long * y)533 void FT2Font::get_bitmap_offset(long *x, long *y)
534 {
535     *x = bbox.xMin;
536     *y = 0;
537 }
538 
draw_glyphs_to_bitmap(bool antialiased)539 void FT2Font::draw_glyphs_to_bitmap(bool antialiased)
540 {
541     size_t width = (bbox.xMax - bbox.xMin) / 64 + 2;
542     size_t height = (bbox.yMax - bbox.yMin) / 64 + 2;
543 
544     image.resize(width, height);
545 
546     for (size_t n = 0; n < glyphs.size(); n++) {
547         FT_Error error = FT_Glyph_To_Bitmap(
548             &glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
549         if (error) {
550             throw_ft_error("Could not convert glyph to bitmap", error);
551         }
552 
553         FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n];
554         // now, draw to our target surface (convert position)
555 
556         // bitmap left and top in pixel, string bbox in subpixel
557         FT_Int x = (FT_Int)(bitmap->left - (bbox.xMin / 64.));
558         FT_Int y = (FT_Int)((bbox.yMax / 64.) - bitmap->top + 1);
559 
560         image.draw_bitmap(&bitmap->bitmap, x, y);
561     }
562 }
563 
get_xys(bool antialiased,std::vector<double> & xys)564 void FT2Font::get_xys(bool antialiased, std::vector<double> &xys)
565 {
566     for (size_t n = 0; n < glyphs.size(); n++) {
567 
568         FT_Error error = FT_Glyph_To_Bitmap(
569             &glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1);
570         if (error) {
571             throw_ft_error("Could not convert glyph to bitmap", error);
572         }
573 
574         FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n];
575 
576         // bitmap left and top in pixel, string bbox in subpixel
577         FT_Int x = (FT_Int)(bitmap->left - bbox.xMin / 64.);
578         FT_Int y = (FT_Int)(bbox.yMax / 64. - bitmap->top + 1);
579         // make sure the index is non-neg
580         x = x < 0 ? 0 : x;
581         y = y < 0 ? 0 : y;
582         xys.push_back(x);
583         xys.push_back(y);
584     }
585 }
586 
draw_glyph_to_bitmap(FT2Image & im,int x,int y,size_t glyphInd,bool antialiased)587 void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased)
588 {
589     FT_Vector sub_offset;
590     sub_offset.x = 0; // int((xd - (double)x) * 64.0);
591     sub_offset.y = 0; // int((yd - (double)y) * 64.0);
592 
593     if (glyphInd >= glyphs.size()) {
594         throw std::runtime_error("glyph num is out of range");
595     }
596 
597     FT_Error error = FT_Glyph_To_Bitmap(
598       &glyphs[glyphInd],
599       antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO,
600       &sub_offset, // additional translation
601       1 // destroy image
602       );
603     if (error) {
604         throw_ft_error("Could not convert glyph to bitmap", error);
605     }
606 
607     FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyphInd];
608 
609     im.draw_bitmap(&bitmap->bitmap, x + bitmap->left, y);
610 }
611 
get_glyph_name(unsigned int glyph_number,char * buffer)612 void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer)
613 {
614     if (!FT_HAS_GLYPH_NAMES(face)) {
615         /* Note that this generated name must match the name that
616            is generated by ttconv in ttfont_CharStrings_getname. */
617         PyOS_snprintf(buffer, 128, "uni%08x", glyph_number);
618     } else {
619         if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer, 128)) {
620             throw_ft_error("Could not get glyph names", error);
621         }
622     }
623 }
624 
get_name_index(char * name)625 long FT2Font::get_name_index(char *name)
626 {
627     return FT_Get_Name_Index(face, (FT_String *)name);
628 }
629