PageRenderTime 66ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/src/_ttconv.cpp

https://github.com/tris-sondon/matplotlib
C++ | 328 lines | 279 code | 39 blank | 10 comment | 25 complexity | 3b12b1a72e82ec0b65b83a4a100a0368 MD5 | raw file
  1. /* -*- mode: c++; c-basic-offset: 4 -*- */
  2. /*
  3. _ttconv.c
  4. Python wrapper for TrueType conversion library in ../ttconv.
  5. */
  6. #include "mplutils.h"
  7. #include <Python.h>
  8. #include "ttconv/pprdrv.h"
  9. #include <vector>
  10. #include <cassert>
  11. class PythonExceptionOccurred
  12. {
  13. };
  14. /**
  15. * An implementation of TTStreamWriter that writes to a Python
  16. * file-like object.
  17. */
  18. class PythonFileWriter : public TTStreamWriter
  19. {
  20. PyObject* _write_method;
  21. public:
  22. PythonFileWriter()
  23. {
  24. _write_method = NULL;
  25. }
  26. ~PythonFileWriter()
  27. {
  28. Py_XDECREF(_write_method);
  29. }
  30. void set(PyObject* write_method)
  31. {
  32. Py_XDECREF(_write_method);
  33. _write_method = write_method;
  34. Py_XINCREF(_write_method);
  35. }
  36. virtual void write(const char* a)
  37. {
  38. PyObject* result = NULL;
  39. if (_write_method)
  40. {
  41. PyObject* decoded = NULL;
  42. decoded = PyUnicode_DecodeLatin1(a, strlen(a), "");
  43. if (decoded == NULL) {
  44. throw PythonExceptionOccurred();
  45. }
  46. result = PyObject_CallFunction(_write_method, "O", decoded);
  47. Py_DECREF(decoded);
  48. if (! result)
  49. {
  50. throw PythonExceptionOccurred();
  51. }
  52. Py_DECREF(result);
  53. }
  54. }
  55. };
  56. int fileobject_to_PythonFileWriter(PyObject* object, void* address)
  57. {
  58. PythonFileWriter* file_writer = (PythonFileWriter*)address;
  59. PyObject* write_method = PyObject_GetAttrString(object, "write");
  60. if (write_method == NULL || ! PyCallable_Check(write_method))
  61. {
  62. PyErr_SetString(PyExc_TypeError, "Expected a file-like object with a write method.");
  63. return 0;
  64. }
  65. file_writer->set(write_method);
  66. Py_DECREF(write_method);
  67. return 1;
  68. }
  69. int pyiterable_to_vector_int(PyObject* object, void* address)
  70. {
  71. std::vector<int>* result = (std::vector<int>*)address;
  72. PyObject* iterator = PyObject_GetIter(object);
  73. if (! iterator)
  74. {
  75. return 0;
  76. }
  77. PyObject* item;
  78. while ((item = PyIter_Next(iterator)))
  79. {
  80. #if PY3K
  81. long value = PyLong_AsLong(item);
  82. #else
  83. long value = PyInt_AsLong(item);
  84. #endif
  85. Py_DECREF(item);
  86. if (value == -1 && PyErr_Occurred())
  87. {
  88. return 0;
  89. }
  90. result->push_back(value);
  91. }
  92. Py_DECREF(iterator);
  93. return 1;
  94. }
  95. static PyObject*
  96. convert_ttf_to_ps(PyObject* self, PyObject* args, PyObject* kwds)
  97. {
  98. const char* filename;
  99. PythonFileWriter output;
  100. int fonttype;
  101. std::vector<int> glyph_ids;
  102. static const char *kwlist[] =
  103. {
  104. "filename", "output", "fonttype", "glyph_ids", NULL
  105. };
  106. if (! PyArg_ParseTupleAndKeywords
  107. (args, kwds,
  108. #if PY_MAJOR_VERSION == 3
  109. "yO&i|O&:convert_ttf_to_ps",
  110. #else
  111. "sO&i|O&:convert_ttf_to_ps",
  112. #endif
  113. (char**)kwlist,
  114. &filename,
  115. fileobject_to_PythonFileWriter,
  116. &output,
  117. &fonttype,
  118. pyiterable_to_vector_int,
  119. &glyph_ids))
  120. {
  121. return NULL;
  122. }
  123. if (fonttype != 3 && fonttype != 42)
  124. {
  125. PyErr_SetString(PyExc_ValueError,
  126. "fonttype must be either 3 (raw Postscript) or 42 "
  127. "(embedded Truetype)");
  128. return NULL;
  129. }
  130. try
  131. {
  132. insert_ttfont(filename, output, (font_type_enum)fonttype, glyph_ids);
  133. }
  134. catch (TTException& e)
  135. {
  136. PyErr_SetString(PyExc_RuntimeError, e.getMessage());
  137. return NULL;
  138. }
  139. catch (PythonExceptionOccurred&)
  140. {
  141. return NULL;
  142. }
  143. catch (...)
  144. {
  145. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  146. return NULL;
  147. }
  148. Py_INCREF(Py_None);
  149. return Py_None;
  150. }
  151. class PythonDictionaryCallback : public TTDictionaryCallback
  152. {
  153. PyObject* _dict;
  154. public:
  155. PythonDictionaryCallback(PyObject* dict)
  156. {
  157. _dict = dict;
  158. }
  159. virtual void add_pair(const char* a, const char* b)
  160. {
  161. assert(a != NULL);
  162. assert(b != NULL);
  163. PyObject* value = PyBytes_FromString(b);
  164. if (!value)
  165. {
  166. throw PythonExceptionOccurred();
  167. }
  168. if (PyDict_SetItemString(_dict, a, value))
  169. {
  170. Py_DECREF(value);
  171. throw PythonExceptionOccurred();
  172. }
  173. Py_DECREF(value);
  174. }
  175. };
  176. static PyObject*
  177. py_get_pdf_charprocs(PyObject* self, PyObject* args, PyObject* kwds)
  178. {
  179. const char* filename;
  180. std::vector<int> glyph_ids;
  181. PyObject* result;
  182. static const char *kwlist[] = { "filename", "glyph_ids", NULL };
  183. if (! PyArg_ParseTupleAndKeywords
  184. (args, kwds,
  185. #if PY_MAJOR_VERSION == 3
  186. "y|O&:get_pdf_charprocs",
  187. #else
  188. "s|O&:get_pdf_charprocs",
  189. #endif
  190. (char **)kwlist,
  191. &filename,
  192. pyiterable_to_vector_int,
  193. &glyph_ids))
  194. {
  195. return NULL;
  196. }
  197. result = PyDict_New();
  198. if (!result)
  199. {
  200. return NULL;
  201. }
  202. PythonDictionaryCallback dict(result);
  203. try
  204. {
  205. ::get_pdf_charprocs(filename, glyph_ids, dict);
  206. }
  207. catch (TTException& e)
  208. {
  209. Py_DECREF(result);
  210. PyErr_SetString(PyExc_RuntimeError, e.getMessage());
  211. return NULL;
  212. }
  213. catch (PythonExceptionOccurred&)
  214. {
  215. Py_DECREF(result);
  216. return NULL;
  217. }
  218. catch (...)
  219. {
  220. Py_DECREF(result);
  221. PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
  222. return NULL;
  223. }
  224. return result;
  225. }
  226. static PyMethodDef ttconv_methods[] =
  227. {
  228. {
  229. "convert_ttf_to_ps", (PyCFunction)convert_ttf_to_ps, METH_VARARGS | METH_KEYWORDS,
  230. "convert_ttf_to_ps(filename, output, fonttype, glyph_ids)\n"
  231. "\n"
  232. "Converts the Truetype font into a Type 3 or Type 42 Postscript font, "
  233. "optionally subsetting the font to only the desired set of characters.\n"
  234. "\n"
  235. "filename is the path to a TTF font file.\n"
  236. "output is a Python file-like object with a write method that the Postscript "
  237. "font data will be written to.\n"
  238. "fonttype may be either 3 or 42. Type 3 is a \"raw Postscript\" font. "
  239. "Type 42 is an embedded Truetype font. Glyph subsetting is not supported "
  240. "for Type 42 fonts.\n"
  241. "glyph_ids (optional) is a list of glyph ids (integers) to keep when "
  242. "subsetting to a Type 3 font. If glyph_ids is not provided or is None, "
  243. "then all glyphs will be included. If any of the glyphs specified are "
  244. "composite glyphs, then the component glyphs will also be included."
  245. },
  246. {
  247. "get_pdf_charprocs", (PyCFunction)py_get_pdf_charprocs, METH_VARARGS | METH_KEYWORDS,
  248. "get_pdf_charprocs(filename, glyph_ids)\n"
  249. "\n"
  250. "Given a Truetype font file, returns a dictionary containing the PDF Type 3\n"
  251. "representation of its paths. Useful for subsetting a Truetype font inside\n"
  252. "of a PDF file.\n"
  253. "\n"
  254. "filename is the path to a TTF font file.\n"
  255. "glyph_ids is a list of the numeric glyph ids to include.\n"
  256. "The return value is a dictionary where the keys are glyph names and\n"
  257. "the values are the stream content needed to render that glyph. This\n"
  258. "is useful to generate the CharProcs dictionary in a PDF Type 3 font.\n"
  259. },
  260. {0, 0, 0, 0} /* Sentinel */
  261. };
  262. static const char* module_docstring =
  263. "Module to handle converting and subsetting TrueType "
  264. "fonts to Postscript Type 3, Postscript Type 42 and "
  265. "Pdf Type 3 fonts.";
  266. #if PY3K
  267. static PyModuleDef ttconv_module = {
  268. PyModuleDef_HEAD_INIT,
  269. "ttconv",
  270. module_docstring,
  271. -1,
  272. ttconv_methods,
  273. NULL, NULL, NULL, NULL
  274. };
  275. PyMODINIT_FUNC
  276. PyInit_ttconv(void)
  277. {
  278. PyObject* m;
  279. m = PyModule_Create(&ttconv_module);
  280. return m;
  281. }
  282. #else
  283. PyMODINIT_FUNC
  284. initttconv(void)
  285. {
  286. Py_InitModule3("ttconv", ttconv_methods, module_docstring);
  287. }
  288. #endif