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