diff --git a/setupext.py b/setupext.py index 7cd5216a59ef..a91c681e0f58 100644 --- a/setupext.py +++ b/setupext.py @@ -468,15 +468,15 @@ def get_extensions(self): cxx_std=11) yield ext # ttconv - ext = Extension( + ext = Pybind11Extension( "matplotlib._ttconv", [ "src/_ttconv.cpp", "extern/ttconv/pprdrv_tt.cpp", "extern/ttconv/pprdrv_tt2.cpp", "extern/ttconv/ttutil.cpp", ], - include_dirs=["extern"]) - add_numpy_flags(ext) + include_dirs=["extern"], + cxx_std=11) yield ext diff --git a/src/_ttconv.cpp b/src/_ttconv.cpp index 929973792f0a..72fdfba6961d 100644 --- a/src/_ttconv.cpp +++ b/src/_ttconv.cpp @@ -5,14 +5,13 @@ Python wrapper for TrueType conversion library in ../ttconv. */ -#define PY_SSIZE_T_CLEAN #include "mplutils.h" -#include +#include #include "ttconv/pprdrv.h" -#include "py_exceptions.h" #include -#include + +namespace py = pybind11; /** * An implementation of TTStreamWriter that writes to a Python @@ -20,142 +19,66 @@ */ class PythonFileWriter : public TTStreamWriter { - PyObject *_write_method; + py::function _write_method; public: - PythonFileWriter() - { - _write_method = NULL; - } - - ~PythonFileWriter() - { - Py_XDECREF(_write_method); - } - - void set(PyObject *write_method) - { - Py_XDECREF(_write_method); - _write_method = write_method; - Py_XINCREF(_write_method); - } + PythonFileWriter(py::object& file_object) + : _write_method(file_object.attr("write")) {} virtual void write(const char *a) { - PyObject *result = NULL; - if (_write_method) { - PyObject *decoded = NULL; - decoded = PyUnicode_DecodeLatin1(a, strlen(a), ""); - if (decoded == NULL) { - throw py::exception(); - } - result = PyObject_CallFunctionObjArgs(_write_method, decoded, NULL); - Py_DECREF(decoded); - if (!result) { - throw py::exception(); - } - Py_DECREF(result); + PyObject* decoded = PyUnicode_DecodeLatin1(a, strlen(a), ""); + if (decoded == NULL) { + throw py::error_already_set(); } + _write_method(py::handle(decoded)); + Py_DECREF(decoded); } }; -int fileobject_to_PythonFileWriter(PyObject *object, void *address) +static void convert_ttf_to_ps( + const char *filename, + py::object &output, + int fonttype, + py::iterable* glyph_ids) { - PythonFileWriter *file_writer = (PythonFileWriter *)address; + PythonFileWriter output_(output); - PyObject *write_method = PyObject_GetAttrString(object, "write"); - if (write_method == NULL || !PyCallable_Check(write_method)) { - PyErr_SetString(PyExc_TypeError, "Expected a file-like object with a write method."); - return 0; - } - - file_writer->set(write_method); - Py_DECREF(write_method); - - return 1; -} - -int pyiterable_to_vector_int(PyObject *object, void *address) -{ - std::vector *result = (std::vector *)address; - - PyObject *iterator = PyObject_GetIter(object); - if (!iterator) { - return 0; - } - - PyObject *item; - while ((item = PyIter_Next(iterator))) { - long value = PyLong_AsLong(item); - Py_DECREF(item); - if (value == -1 && PyErr_Occurred()) { - return 0; + std::vector glyph_ids_; + if (glyph_ids) { + for (py::handle glyph_id: *glyph_ids) { + glyph_ids_.push_back(glyph_id.cast()); } - result->push_back((int)value); - } - - Py_DECREF(iterator); - - return 1; -} - -static PyObject *convert_ttf_to_ps(PyObject *self, PyObject *args, PyObject *kwds) -{ - const char *filename; - PythonFileWriter output; - int fonttype; - std::vector glyph_ids; - - static const char *kwlist[] = { "filename", "output", "fonttype", "glyph_ids", NULL }; - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "yO&i|O&:convert_ttf_to_ps", - (char **)kwlist, - &filename, - fileobject_to_PythonFileWriter, - &output, - &fonttype, - pyiterable_to_vector_int, - &glyph_ids)) { - return NULL; } if (fonttype != 3 && fonttype != 42) { - PyErr_SetString(PyExc_ValueError, - "fonttype must be either 3 (raw Postscript) or 42 " - "(embedded Truetype)"); - return NULL; + throw py::value_error( + "fonttype must be either 3 (raw Postscript) or 42 (embedded Truetype)"); } try { - insert_ttfont(filename, output, (font_type_enum)fonttype, glyph_ids); + insert_ttfont(filename, output_, static_cast(fonttype), glyph_ids_); } catch (TTException &e) { - PyErr_SetString(PyExc_RuntimeError, e.getMessage()); - return NULL; - } - catch (const py::exception &) - { - return NULL; + throw std::runtime_error(e.getMessage()); } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception"); - return NULL; + throw std::runtime_error("Unknown C++ exception"); } - - Py_INCREF(Py_None); - return Py_None; } -static PyMethodDef ttconv_methods[] = -{ - { - "convert_ttf_to_ps", (PyCFunction)convert_ttf_to_ps, METH_VARARGS | METH_KEYWORDS, - "convert_ttf_to_ps(filename, output, fonttype, glyph_ids)\n" - "\n" +PYBIND11_MODULE(_ttconv, m) { + m.doc() = "Module to handle converting and subsetting TrueType " + "fonts to Postscript Type 3, Postscript Type 42 and " + "Pdf Type 3 fonts."; + m.def("convert_ttf_to_ps", &convert_ttf_to_ps, + py::arg("filename"), + py::arg("output"), + py::arg("fonttype"), + py::arg("glyph_ids") = py::none(), "Converts the Truetype font into a Type 3 or Type 42 Postscript font, " "optionally subsetting the font to only the desired set of characters.\n" "\n" @@ -169,25 +92,5 @@ static PyMethodDef ttconv_methods[] = "subsetting to a Type 3 font. If glyph_ids is not provided or is None, " "then all glyphs will be included. If any of the glyphs specified are " "composite glyphs, then the component glyphs will also be included." - }, - {0, 0, 0, 0} /* Sentinel */ -}; - -static const char *module_docstring = - "Module to handle converting and subsetting TrueType " - "fonts to Postscript Type 3, Postscript Type 42 and " - "Pdf Type 3 fonts."; - -static PyModuleDef ttconv_module = { - PyModuleDef_HEAD_INIT, - "ttconv", - module_docstring, - -1, - ttconv_methods, -}; - -PyMODINIT_FUNC -PyInit__ttconv(void) -{ - return PyModule_Create(&ttconv_module); + ); }