Skip to content

Commit 5f2a89a

Browse files
committed
Move Python code from ft2font to its wrapper
This improves the encapsulation and separation of concerns between the files.
1 parent 25be2f1 commit 5f2a89a

File tree

3 files changed

+90
-77
lines changed

3 files changed

+90
-77
lines changed

src/ft2font.cpp

Lines changed: 26 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
/* -*- mode: c++; c-basic-offset: 4 -*- */
22

3-
#define NO_IMPORT_ARRAY
4-
53
#include <algorithm>
4+
#include <charconv>
65
#include <iterator>
76
#include <set>
87
#include <sstream>
98
#include <stdexcept>
109
#include <string>
10+
#include <vector>
1111

1212
#include "ft2font.h"
1313
#include "mplutils.h"
14-
#include "numpy_cpp.h"
15-
#include "py_exceptions.h"
1614

1715
#ifndef M_PI
1816
#define M_PI 3.14159265358979323846264338328
@@ -185,30 +183,6 @@ FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1,
185183
m_dirty = true;
186184
}
187185

188-
static void ft_glyph_warn(FT_ULong charcode, std::set<FT_String*> family_names)
189-
{
190-
PyObject *text_helpers = NULL, *tmp = NULL;
191-
std::set<FT_String*>::iterator it = family_names.begin();
192-
std::stringstream ss;
193-
ss<<*it;
194-
while(++it != family_names.end()){
195-
ss<<", "<<*it;
196-
}
197-
198-
if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) ||
199-
!(tmp = PyObject_CallMethod(text_helpers,
200-
"warn_on_missing_glyph", "(k, s)",
201-
charcode, ss.str().c_str()))) {
202-
goto exit;
203-
}
204-
exit:
205-
Py_XDECREF(text_helpers);
206-
Py_XDECREF(tmp);
207-
if (PyErr_Occurred()) {
208-
throw mpl::exception();
209-
}
210-
}
211-
212186
// ft_outline_decomposer should be passed to FT_Outline_Decompose. On the
213187
// first pass, vertices and codes are set to NULL, and index is simply
214188
// incremented for each vertex that should be inserted, so that it is set, at
@@ -296,52 +270,41 @@ static FT_Outline_Funcs ft_outline_funcs = {
296270
ft_outline_conic_to,
297271
ft_outline_cubic_to};
298272

299-
PyObject*
300-
FT2Font::get_path()
273+
void
274+
FT2Font::get_path(std::vector<double> &vertices, std::vector<unsigned char> &codes)
301275
{
302276
if (!face->glyph) {
303-
PyErr_SetString(PyExc_RuntimeError, "No glyph loaded");
304-
return NULL;
277+
throw std::runtime_error("No glyph loaded");
305278
}
306279
ft_outline_decomposer decomposer = {};
307-
if (FT_Error error =
308-
FT_Outline_Decompose(
309-
&face->glyph->outline, &ft_outline_funcs, &decomposer)) {
310-
PyErr_Format(PyExc_RuntimeError,
311-
"FT_Outline_Decompose failed with error 0x%x", error);
312-
return NULL;
280+
if (FT_Error error = FT_Outline_Decompose(
281+
&face->glyph->outline, &ft_outline_funcs, &decomposer)) {
282+
throw std::runtime_error("FT_Outline_Decompose failed with error " +
283+
std::to_string(error));
313284
}
314285
if (!decomposer.index) { // Don't append CLOSEPOLY to null glyphs.
315-
npy_intp vertices_dims[2] = { 0, 2 };
316-
numpy::array_view<double, 2> vertices(vertices_dims);
317-
npy_intp codes_dims[1] = { 0 };
318-
numpy::array_view<unsigned char, 1> codes(codes_dims);
319-
return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj());
320-
}
321-
npy_intp vertices_dims[2] = { decomposer.index + 1, 2 };
322-
numpy::array_view<double, 2> vertices(vertices_dims);
323-
npy_intp codes_dims[1] = { decomposer.index + 1 };
324-
numpy::array_view<unsigned char, 1> codes(codes_dims);
286+
return;
287+
}
288+
vertices.resize((decomposer.index + 1) * 2);
289+
codes.resize(decomposer.index + 1);
325290
decomposer.index = 0;
326291
decomposer.vertices = vertices.data();
327292
decomposer.codes = codes.data();
328-
if (FT_Error error =
329-
FT_Outline_Decompose(
330-
&face->glyph->outline, &ft_outline_funcs, &decomposer)) {
331-
PyErr_Format(PyExc_RuntimeError,
332-
"FT_Outline_Decompose failed with error 0x%x", error);
333-
return NULL;
293+
if (FT_Error error = FT_Outline_Decompose(
294+
&face->glyph->outline, &ft_outline_funcs, &decomposer)) {
295+
throw std::runtime_error("FT_Outline_Decompose failed with error " +
296+
std::to_string(error));
334297
}
335298
*(decomposer.vertices++) = 0;
336299
*(decomposer.vertices++) = 0;
337300
*(decomposer.codes++) = CLOSEPOLY;
338-
return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj());
339301
}
340302

341303
FT2Font::FT2Font(FT_Open_Args &open_args,
342304
long hinting_factor_,
343-
std::vector<FT2Font *> &fallback_list)
344-
: image(), face(NULL)
305+
std::vector<FT2Font *> &fallback_list,
306+
FT2Font::WarnFunc warn)
307+
: ft_glyph_warn(warn), image(), face(NULL)
345308
{
346309
clear();
347310

@@ -819,7 +782,8 @@ void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd,
819782
im.draw_bitmap(&bitmap->bitmap, x + bitmap->left, y);
820783
}
821784

822-
void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback = false)
785+
void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer,
786+
bool fallback = false)
823787
{
824788
if (fallback && glyph_to_font.find(glyph_number) != glyph_to_font.end()) {
825789
// cache is only for parent FT2Font
@@ -830,9 +794,11 @@ void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallb
830794
if (!FT_HAS_GLYPH_NAMES(face)) {
831795
/* Note that this generated name must match the name that
832796
is generated by ttconv in ttfont_CharStrings_getname. */
833-
PyOS_snprintf(buffer, 128, "uni%08x", glyph_number);
797+
buffer.replace(0, 3, "uni");
798+
std::to_chars(buffer.data() + 3, buffer.data() + buffer.size(),
799+
glyph_number, 16);
834800
} else {
835-
if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer, 128)) {
801+
if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer.data(), buffer.size())) {
836802
throw_ft_error("Could not get glyph names", error);
837803
}
838804
}

src/ft2font.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <cstdint>
1313
#include <set>
14+
#include <string>
1415
#include <unordered_map>
1516
#include <vector>
1617

@@ -71,9 +72,11 @@ extern FT_Library _ft2Library;
7172

7273
class FT2Font
7374
{
75+
typedef void (*WarnFunc)(FT_ULong charcode, std::set<FT_String*> family_names);
7476

7577
public:
76-
FT2Font(FT_Open_Args &open_args, long hinting_factor, std::vector<FT2Font *> &fallback_list);
78+
FT2Font(FT_Open_Args &open_args, long hinting_factor,
79+
std::vector<FT2Font *> &fallback_list, WarnFunc warn);
7780
virtual ~FT2Font();
7881
void clear();
7982
void set_size(double ptsize, double dpi);
@@ -106,10 +109,10 @@ class FT2Font
106109
void get_xys(bool antialiased, std::vector<double> &xys);
107110
void draw_glyphs_to_bitmap(bool antialiased);
108111
void draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased);
109-
void get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback);
112+
void get_glyph_name(unsigned int glyph_number, std::string &buffer, bool fallback);
110113
long get_name_index(char *name);
111114
FT_UInt get_char_index(FT_ULong charcode, bool fallback);
112-
PyObject* get_path();
115+
void get_path(std::vector<double> &vertices, std::vector<unsigned char> &codes);
113116
bool get_char_fallback_index(FT_ULong charcode, int& index) const;
114117

115118
FT_Face const &get_face() const
@@ -143,6 +146,7 @@ class FT2Font
143146
}
144147

145148
private:
149+
WarnFunc ft_glyph_warn;
146150
FT2Image image;
147151
FT_Face face;
148152
FT_Vector pen; /* untransformed origin */

src/ft2font_wrapper.cpp

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#include "mplutils.h"
22
#include "ft2font.h"
3+
#include "numpy_cpp.h"
34
#include "py_converters.h"
45
#include "py_exceptions.h"
56

67
// From Python
78
#include <structmember.h>
89

10+
#include <sstream>
911
#include <set>
1012

1113
static PyObject *convert_xys_to_array(std::vector<double> &xys)
@@ -308,6 +310,31 @@ static void close_file_callback(FT_Stream stream)
308310
PyErr_Restore(type, value, traceback);
309311
}
310312

313+
static void
314+
ft_glyph_warn(FT_ULong charcode, std::set<FT_String*> family_names)
315+
{
316+
PyObject *text_helpers = NULL, *tmp = NULL;
317+
std::set<FT_String*>::iterator it = family_names.begin();
318+
std::stringstream ss;
319+
ss<<*it;
320+
while(++it != family_names.end()){
321+
ss<<", "<<*it;
322+
}
323+
324+
if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) ||
325+
!(tmp = PyObject_CallMethod(text_helpers,
326+
"warn_on_missing_glyph", "(k, s)",
327+
charcode, ss.str().c_str()))) {
328+
goto exit;
329+
}
330+
exit:
331+
Py_XDECREF(text_helpers);
332+
Py_XDECREF(tmp);
333+
if (PyErr_Occurred()) {
334+
throw mpl::exception();
335+
}
336+
}
337+
311338
static PyObject *PyFT2Font_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
312339
{
313340
PyFT2Font *self;
@@ -455,7 +482,8 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds)
455482
Py_CLEAR(data);
456483

457484
CALL_CPP_FULL(
458-
"FT2Font", (self->x = new FT2Font(open_args, hinting_factor, fallback_fonts)),
485+
"FT2Font",
486+
(self->x = new FT2Font(open_args, hinting_factor, fallback_fonts, ft_glyph_warn)),
459487
Py_CLEAR(self->py_file), -1);
460488

461489
CALL_CPP_INIT("FT2Font->set_kerning_factor", (self->x->set_kerning_factor(kerning_factor)));
@@ -555,13 +583,13 @@ static PyObject *PyFT2Font_get_kerning(PyFT2Font *self, PyObject *args)
555583
{
556584
FT_UInt left, right, mode;
557585
int result;
558-
int fallback = 1;
586+
bool fallback = true;
559587

560588
if (!PyArg_ParseTuple(args, "III:get_kerning", &left, &right, &mode)) {
561589
return NULL;
562590
}
563591

564-
CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode, (bool)fallback)));
592+
CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode, fallback)));
565593

566594
return PyLong_FromLong(result);
567595
}
@@ -704,7 +732,7 @@ const char *PyFT2Font_load_char__doc__ =
704732
static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject *kwds)
705733
{
706734
long charcode;
707-
int fallback = 1;
735+
bool fallback = true;
708736
FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT;
709737
const char *names[] = { "charcode", "flags", NULL };
710738

@@ -717,7 +745,7 @@ static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject *
717745
}
718746

719747
FT2Font *ft_object = NULL;
720-
CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback)));
748+
CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, fallback)));
721749

722750
return PyGlyph_from_FT2Font(ft_object);
723751
}
@@ -743,7 +771,7 @@ static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject
743771
{
744772
FT_UInt glyph_index;
745773
FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT;
746-
int fallback = 1;
774+
bool fallback = true;
747775
const char *names[] = { "glyph_index", "flags", NULL };
748776

749777
/* This makes a technically incorrect assumption that FT_Int32 is
@@ -755,7 +783,7 @@ static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject
755783
}
756784

757785
FT2Font *ft_object = NULL;
758-
CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, (bool)fallback)));
786+
CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, fallback)));
759787

760788
return PyGlyph_from_FT2Font(ft_object);
761789
}
@@ -912,14 +940,16 @@ const char *PyFT2Font_get_glyph_name__doc__ =
912940
static PyObject *PyFT2Font_get_glyph_name(PyFT2Font *self, PyObject *args)
913941
{
914942
unsigned int glyph_number;
915-
char buffer[128];
916-
int fallback = 1;
943+
std::string buffer;
944+
bool fallback = true;
917945

918946
if (!PyArg_ParseTuple(args, "I:get_glyph_name", &glyph_number)) {
919947
return NULL;
920948
}
921-
CALL_CPP("get_glyph_name", (self->x->get_glyph_name(glyph_number, buffer, (bool)fallback)));
922-
return PyUnicode_FromString(buffer);
949+
buffer.resize(128);
950+
CALL_CPP("get_glyph_name",
951+
(self->x->get_glyph_name(glyph_number, buffer, fallback)));
952+
return PyUnicode_FromString(buffer.c_str());
923953
}
924954

925955
const char *PyFT2Font_get_charmap__doc__ =
@@ -962,13 +992,13 @@ static PyObject *PyFT2Font_get_char_index(PyFT2Font *self, PyObject *args)
962992
{
963993
FT_UInt index;
964994
FT_ULong ccode;
965-
int fallback = 1;
995+
bool fallback = true;
966996

967997
if (!PyArg_ParseTuple(args, "k:get_char_index", &ccode)) {
968998
return NULL;
969999
}
9701000

971-
CALL_CPP("get_char_index", index = self->x->get_char_index(ccode, (bool)fallback));
1001+
CALL_CPP("get_char_index", index = self->x->get_char_index(ccode, fallback));
9721002

9731003
return PyLong_FromLong(index);
9741004
}
@@ -1270,7 +1300,20 @@ const char *PyFT2Font_get_path__doc__ =
12701300

12711301
static PyObject *PyFT2Font_get_path(PyFT2Font *self, PyObject *args)
12721302
{
1273-
CALL_CPP("get_path", return self->x->get_path());
1303+
std::vector<double> vertices;
1304+
std::vector<unsigned char> codes;
1305+
1306+
CALL_CPP("get_path", self->x->get_path(vertices, codes));
1307+
1308+
npy_intp length = codes.size();
1309+
npy_intp vertices_dims[2] = { length, 2 };
1310+
numpy::array_view<double, 2> vertices_arr(vertices_dims);
1311+
memcpy(vertices_arr.data(), vertices.data(), sizeof(double) * vertices.size());
1312+
npy_intp codes_dims[1] = { length };
1313+
numpy::array_view<unsigned char, 1> codes_arr(codes_dims);
1314+
memcpy(codes_arr.data(), codes.data(), codes.size());
1315+
1316+
return Py_BuildValue("NN", vertices_arr.pyobj(), codes_arr.pyobj());
12741317
}
12751318

12761319
const char *PyFT2Font_get_image__doc__ =

0 commit comments

Comments
 (0)