/* -*- mode: c++; c-basic-offset: 4 -*- */
/*
_ttconv.c
Python wrapper for TrueType conversion library in ../ttconv.
*/
#define PY_SSIZE_T_CLEAN
#include "mplutils.h"
#include <Python.h>
#include "ttconv/pprdrv.h"
#include "py_exceptions.h"
#include <vector>
#include <cassert>
/**
* An implementation of TTStreamWriter that writes to a Python
* file-like object.
*/
class PythonFileWriter : public TTStreamWriter
{
PyObject *_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);
}
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_CallFunction(_write_method, (char *)"O", decoded);
Py_DECREF(decoded);
if (!result) {
throw py::exception();
}
Py_DECREF(result);
}
}
};
int fileobject_to_PythonFileWriter(PyObject *object, void *address)
{
PythonFileWriter *file_writer = (PythonFileWriter *)address;
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<int> *result = (std::vector<int> *)address;
PyObject *iterator = PyObject_GetIter(object);
if (!iterator) {
return 0;
}
PyObject *item;
while ((item = PyIter_Next(iterator))) {
#if PY3K
long value = PyLong_AsLong(item);
#else
long value = PyInt_AsLong(item);
#endif
Py_DECREF(item);
if (value == -1 && PyErr_Occurred()) {
return 0;
}
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<int> glyph_ids;
static const char *kwlist[] = { "filename", "output", "fonttype", "glyph_ids", NULL };
if (!PyArg_ParseTupleAndKeywords(args,
kwds,
#if PY_MAJOR_VERSION == 3
"yO&i|O&:convert_ttf_to_ps",
#else
"sO&i|O&:convert_ttf_to_ps",
#endif
(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;
}
try
{
insert_ttfont(filename, output, (font_type_enum)fonttype, glyph_ids);
}
catch (TTException &e)
{
PyErr_SetString(PyExc_RuntimeError, e.getMessage());
return NULL;
}
catch (const py::exception &)
{
return NULL;
}
catch (...)
{
PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
class PythonDictionaryCallback : public TTDictionaryCallback
{
PyObject *_dict;
public:
PythonDictionaryCallback(PyObject *dict)
{
_dict = dict;
}
virtual void add_pair(const char *a, const char *b)
{
assert(a != NULL);
assert(b != NULL);
PyObject *value = PyBytes_FromString(b);
if (!value) {
throw py::exception();
}
if (PyDict_SetItemString(_dict, a, value)) {
Py_DECREF(value);
throw py::exception();
}
Py_DECREF(value);
}
};
static PyObject *py_get_pdf_charprocs(PyObject *self, PyObject *args, PyObject *kwds)
{
const char *filename;
std::vector<int> glyph_ids;
PyObject *result;
static const char *kwlist[] = { "filename", "glyph_ids", NULL };
if (!PyArg_ParseTupleAndKeywords(args,
kwds,
#if PY_MAJOR_VERSION == 3
"y|O&:get_pdf_charprocs",
#else
"s|O&:get_pdf_charprocs",
#endif
(char **)kwlist,
&filename,
pyiterable_to_vector_int,
&glyph_ids)) {
return NULL;
}
result = PyDict_New();
if (!result) {
return NULL;
}
PythonDictionaryCallback dict(result);
try
{
::get_pdf_charprocs(filename, glyph_ids, dict);
}
catch (TTException &e)
{
Py_DECREF(result);
PyErr_SetString(PyExc_RuntimeError, e.getMessage());
return NULL;
}
catch (const py::exception &)
{
Py_DECREF(result);
return NULL;
}
catch (...)
{
Py_DECREF(result);
PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
return NULL;
}
return result;
}
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"
"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"
"filename is the path to a TTF font file.\n"
"output is a Python file-like object with a write method that the Postscript "
"font data will be written to.\n"
"fonttype may be either 3 or 42. Type 3 is a \"raw Postscript\" font. "
"Type 42 is an embedded Truetype font. Glyph subsetting is not supported "
"for Type 42 fonts.\n"
"glyph_ids (optional) is a list of glyph ids (integers) to keep when "
"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."
},
{
"get_pdf_charprocs", (PyCFunction)py_get_pdf_charprocs, METH_VARARGS | METH_KEYWORDS,
"get_pdf_charprocs(filename, glyph_ids)\n"
"\n"
"Given a Truetype font file, returns a dictionary containing the PDF Type 3\n"
"representation of its paths. Useful for subsetting a Truetype font inside\n"
"of a PDF file.\n"
"\n"
"filename is the path to a TTF font file.\n"
"glyph_ids is a list of the numeric glyph ids to include.\n"
"The return value is a dictionary where the keys are glyph names and\n"
"the values are the stream content needed to render that glyph. This\n"
"is useful to generate the CharProcs dictionary in a PDF Type 3 font.\n"
},
{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.";
#if PY3K
static PyModuleDef ttconv_module = {
PyModuleDef_HEAD_INIT,
"ttconv",
module_docstring,
-1,
ttconv_methods,
NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC
PyInit_ttconv(void)
{
PyObject* m;
m = PyModule_Create(&ttconv_module);
return m;
}
#else
PyMODINIT_FUNC
initttconv(void)
{
Py_InitModule3("ttconv", ttconv_methods, module_docstring);
}
#endif