|
1 | | -#include "CXX/Extensions.hxx" |
2 | | -#include "CXX/Objects.hxx" |
3 | | -#include "ttconv/pprdrv.h" |
4 | | - |
5 | | -class ttconv_module : public Py::ExtensionModule<ttconv_module> |
6 | | -{ |
7 | | -public: |
8 | | - ttconv_module() |
9 | | - : Py::ExtensionModule<ttconv_module>( "ttconv" ) |
10 | | - { |
11 | | - add_varargs_method("convert_ttf_to_ps", |
12 | | - &ttconv_module::convert_ttf_to_ps, |
13 | | - ttconv_module::convert_ttf_to_ps__doc__); |
14 | | - add_varargs_method("get_pdf_charprocs", |
15 | | - &ttconv_module::get_pdf_charprocs, |
16 | | - ttconv_module::get_pdf_charprocs__doc__); |
17 | | - |
18 | | - initialize( "The ttconv module" ); |
19 | | - } |
20 | | - |
21 | | - Py::Object |
22 | | - convert_ttf_to_ps(const Py::Tuple& args); |
23 | | - static char convert_ttf_to_ps__doc__[]; |
| 1 | +/* |
| 2 | + _ttconv.c |
24 | 3 |
|
25 | | - Py::Object |
26 | | - get_pdf_charprocs(const Py::Tuple& args); |
27 | | - static char get_pdf_charprocs__doc__[]; |
28 | | -}; |
| 4 | + Python wrapper for TrueType conversion library in ../ttconv. |
| 5 | + */ |
29 | 6 |
|
30 | | -char ttconv_module::convert_ttf_to_ps__doc__[] = |
31 | | -"convert_ttf_to_ps(filename, output, fonttype, glyph_ids)\n" |
32 | | -"\n" |
33 | | -"Converts the Truetype font into a Type 3 or Type 42 Postscript font, " |
34 | | -"optionally subsetting the font to only the desired set of characters.\n" |
35 | | -"\n" |
36 | | -"filename is the path to a TTF font file.\n" |
37 | | -"output is a Python file-like object with a write method that the Postscript " |
38 | | -"font data will be written to.\n" |
39 | | -"fonttype may be either 3 or 42. Type 3 is a \"raw Postscript\" font. " |
40 | | -"Type 42 is an embedded Truetype font. Glyph subsetting is not supported " |
41 | | -"for Type 42 fonts.\n" |
42 | | -"glyph_ids (optional) is a list of glyph ids (integers) to keep when " |
43 | | -"subsetting to a Type 3 font. If glyph_ids is not provided or is None, " |
44 | | -"then all glyphs will be included. If any of the glyphs specified are " |
45 | | -"composite glyphs, then the component glyphs will also be included." |
46 | | -; |
| 7 | +#include <Python.h> |
| 8 | +#include "ttconv/pprdrv.h" |
| 9 | +#include <vector> |
47 | 10 |
|
48 | 11 | /** |
49 | 12 | * An implementation of TTStreamWriter that writes to a Python |
50 | 13 | * file-like object. |
51 | 14 | */ |
52 | 15 | class PythonFileWriter : public TTStreamWriter { |
53 | | - Py::Callable _write_method; |
| 16 | + PyObject* _write_method; |
54 | 17 |
|
55 | 18 | public: |
56 | | - PythonFileWriter(const Py::Object& file_like_object) { |
57 | | - _write_method = file_like_object.getAttr( "write" ); |
| 19 | + PythonFileWriter() { |
| 20 | + _write_method = NULL; |
58 | 21 | } |
59 | 22 |
|
60 | | - virtual void write(const char* a) { |
61 | | - Py::Tuple args(1); |
62 | | - args[0] = Py::String(a); |
63 | | - _write_method.apply(args); |
| 23 | + ~PythonFileWriter() { |
| 24 | + if (_write_method) |
| 25 | + Py_DECREF(_write_method); |
64 | 26 | } |
65 | | -}; |
66 | 27 |
|
67 | | -Py::Object |
68 | | -ttconv_module::convert_ttf_to_ps(const Py::Tuple & args) { |
69 | | - args.verify_length(3, 4); |
| 28 | + void set(PyObject* write_method) { |
| 29 | + if (_write_method) |
| 30 | + Py_DECREF(_write_method); |
| 31 | + _write_method = write_method; |
| 32 | + if (_write_method) |
| 33 | + Py_INCREF(_write_method); |
| 34 | + } |
70 | 35 |
|
71 | | - std::string fname = Py::String(args[0]).as_std_string(); |
| 36 | + virtual void write(const char* a) { |
| 37 | + if (_write_method) |
| 38 | + PyObject_CallFunction(_write_method, "s", a); |
| 39 | + } |
| 40 | +}; |
72 | 41 |
|
73 | | - PythonFileWriter python_file_writer(args[1]); |
| 42 | +int fileobject_to_PythonFileWriter(PyObject* object, void* address) { |
| 43 | + PythonFileWriter* file_writer = (PythonFileWriter*)address; |
| 44 | + PyObject* write_method = PyObject_GetAttrString(object, "write"); |
| 45 | + if (write_method == NULL || ! PyCallable_Check(write_method)) { |
| 46 | + PyErr_SetString(PyExc_TypeError, "Expected a file-like object with a write method."); |
| 47 | + return 0; |
| 48 | + } |
| 49 | + file_writer->set(write_method); |
| 50 | + return 1; |
| 51 | +} |
74 | 52 |
|
75 | | - long font_type = (long)Py::Int(args[2]); |
76 | | - if ( font_type != 3 && font_type != 42 ) { |
77 | | - throw Py::ValueError("Font type must be either 3 (raw Postscript) or 42 (embedded Truetype)"); |
| 53 | +int pyiterable_to_vector_int(PyObject* object, void* address) { |
| 54 | + std::vector<int>* result = (std::vector<int>*)address; |
| 55 | + PyObject* iterator = PyObject_GetIter(object); |
| 56 | + if (! iterator) |
| 57 | + return 0; |
| 58 | + PyObject* item; |
| 59 | + while (item = PyIter_Next(iterator)) { |
| 60 | + long value = PyInt_AsLong(item); |
| 61 | + if (value == -1 && PyErr_Occurred()) |
| 62 | + return 0; |
| 63 | + result->push_back(value); |
78 | 64 | } |
| 65 | + return 1; |
| 66 | +} |
79 | 67 |
|
80 | | - std::vector<int> glyph_ids; |
81 | | - if ( args.size() == 4 ) { |
82 | | - if ( args[3] != Py::None() ) { |
83 | | - Py::SeqBase< Py::Int > py_glyph_ids = args[3]; |
84 | | - size_t num_glyphs = py_glyph_ids.size(); |
85 | | - // If there are no included glyphs, just return |
86 | | - if (num_glyphs == 0) { |
87 | | - return Py::Object(); |
88 | | - } |
89 | | - glyph_ids.reserve(num_glyphs); |
90 | | - for (size_t i = 0; i < num_glyphs; ++i) { |
91 | | - glyph_ids.push_back( (long) py_glyph_ids.getItem(i) ); |
92 | | - } |
93 | | - } |
| 68 | +static PyObject* |
| 69 | +convert_ttf_to_ps(PyObject* self, PyObject* args, PyObject* kwds) { |
| 70 | + const char* filename; |
| 71 | + PythonFileWriter output; |
| 72 | + int fonttype; |
| 73 | + std::vector<int> glyph_ids; |
| 74 | + |
| 75 | + static char *kwlist[] = { "filename", "output", "fonttype", "glyph_ids", NULL }; |
| 76 | + if (! PyArg_ParseTupleAndKeywords |
| 77 | + (args, kwds, |
| 78 | + "sO&i|O&:convert_ttf_to_ps", kwlist, |
| 79 | + &filename, |
| 80 | + fileobject_to_PythonFileWriter, |
| 81 | + &output, |
| 82 | + &fonttype, |
| 83 | + pyiterable_to_vector_int, |
| 84 | + &glyph_ids)) |
| 85 | + return NULL; |
| 86 | + |
| 87 | + if (fonttype != 3 && fonttype != 42) { |
| 88 | + PyErr_SetString(PyExc_ValueError, |
| 89 | + "fonttype must be either 3 (raw Postscript) or 42 " |
| 90 | + "(embedded Truetype)"); |
| 91 | + return NULL; |
94 | 92 | } |
95 | 93 |
|
96 | 94 | try { |
97 | | - insert_ttfont( fname.c_str(), python_file_writer, (font_type_enum) font_type, glyph_ids ); |
| 95 | + insert_ttfont( filename, output, (font_type_enum)fonttype, glyph_ids ); |
98 | 96 | } catch (TTException& e) { |
99 | | - throw Py::RuntimeError(e.getMessage()); |
| 97 | + PyErr_SetString(PyExc_RuntimeError, e.getMessage()); |
| 98 | + return NULL; |
| 99 | + } catch (...) { |
| 100 | + PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception"); |
| 101 | + return NULL; |
100 | 102 | } |
101 | 103 |
|
102 | | - return Py::Object(); |
| 104 | + Py_INCREF(Py_None); |
| 105 | + return Py_None; |
103 | 106 | } |
104 | 107 |
|
105 | | -char ttconv_module::get_pdf_charprocs__doc__[] = |
106 | | -"get_pdf_charprocs(filename, glyph_ids)\n" |
107 | | -"\n" |
108 | | -"Given a Truetype font file, returns a dictionary containing the PDF Type 3\n" |
109 | | -"representation of its path. Useful for subsetting a Truetype font inside\n" |
110 | | -"of a PDF file.\n" |
111 | | -"\n" |
112 | | -"filename is the path to a TTF font file.\n" |
113 | | -"glyph_ids is a list of the numeric glyph ids to include.\n" |
114 | | -"The return value is a dictionary where the keys are glyph names and \n" |
115 | | -"the values are the stream content needed to render that glyph. This\n" |
116 | | -"is useful to generate the CharProcs dictionary in a PDF Type 3 font.\n" |
117 | | -; |
118 | | - |
119 | | -/** |
120 | | - * An implementation of TTStreamWriter that writes to a Python |
121 | | - * file-like object. |
122 | | - */ |
123 | 108 | class PythonDictionaryCallback : public TTDictionaryCallback { |
124 | | - Py::Dict _dict; |
| 109 | + PyObject* _dict; |
125 | 110 |
|
126 | 111 | public: |
127 | | - PythonDictionaryCallback(const Py::Dict& dict) : _dict(dict) { |
128 | | - |
| 112 | + PythonDictionaryCallback(PyObject* dict) { |
| 113 | + _dict = dict; |
129 | 114 | } |
130 | 115 |
|
131 | 116 | virtual void add_pair(const char* a, const char* b) { |
132 | | - _dict.setItem(a, Py::String(b)); |
| 117 | + PyObject* value = PyString_FromString(b); |
| 118 | + if (value) |
| 119 | + PyDict_SetItemString(_dict, a, value); |
133 | 120 | } |
134 | 121 | }; |
135 | 122 |
|
136 | | -Py::Object |
137 | | -ttconv_module::get_pdf_charprocs(const Py::Tuple & args) { |
138 | | - args.verify_length(1, 2); |
139 | | - |
140 | | - Py::Dict result; |
141 | | - |
142 | | - std::string fname = Py::String(args[0]).as_std_string(); |
143 | | - |
144 | | - std::vector<int> glyph_ids; |
145 | | - if ( args.size() == 2 ) { |
146 | | - if ( args[1] != Py::None() ) { |
147 | | - Py::SeqBase< Py::Int > py_glyph_ids = args[1]; |
148 | | - size_t num_glyphs = py_glyph_ids.size(); |
149 | | - // If there are no included glyphs, just return |
150 | | - if (num_glyphs == 0) { |
151 | | - return result; |
152 | | - } |
153 | | - glyph_ids.reserve(num_glyphs); |
154 | | - for (size_t i = 0; i < num_glyphs; ++i) { |
155 | | - glyph_ids.push_back( (long) py_glyph_ids.getItem(i) ); |
156 | | - } |
157 | | - } |
158 | | - } |
| 123 | +static PyObject* |
| 124 | +py_get_pdf_charprocs(PyObject* self, PyObject* args, PyObject* kwds) { |
| 125 | + const char* filename; |
| 126 | + std::vector<int> glyph_ids; |
| 127 | + PyObject* result; |
| 128 | + |
| 129 | + static char *kwlist[] = { "filename", "glyph_ids", NULL }; |
| 130 | + if (! PyArg_ParseTupleAndKeywords |
| 131 | + (args, kwds, |
| 132 | + "s|O&:convert_ttf_to_ps", kwlist, |
| 133 | + &filename, |
| 134 | + pyiterable_to_vector_int, |
| 135 | + &glyph_ids)) |
| 136 | + return NULL; |
159 | 137 |
|
160 | | - PythonDictionaryCallback dictCallback(result); |
| 138 | + result = PyDict_New(); |
| 139 | + if (!result) |
| 140 | + return NULL; |
| 141 | + |
| 142 | + PythonDictionaryCallback dict(result); |
161 | 143 |
|
162 | 144 | try { |
163 | | - ::get_pdf_charprocs( fname.c_str(), glyph_ids, dictCallback ); |
| 145 | + ::get_pdf_charprocs( filename, glyph_ids, dict ); |
164 | 146 | } catch (TTException& e) { |
165 | | - throw Py::RuntimeError(e.getMessage()); |
| 147 | + PyErr_SetString(PyExc_RuntimeError, e.getMessage()); |
| 148 | + return NULL; |
| 149 | + } catch (...) { |
| 150 | + PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception"); |
| 151 | + return NULL; |
166 | 152 | } |
167 | 153 |
|
168 | 154 | return result; |
169 | 155 | } |
170 | 156 |
|
171 | | -#if defined(_MSC_VER) |
172 | | -DL_EXPORT(void) |
173 | | -#elif defined(__cplusplus) |
174 | | - extern "C" void |
175 | | -#else |
176 | | -void |
| 157 | +static PyMethodDef ttconv_methods[] = { |
| 158 | + {"convert_ttf_to_ps", (PyCFunction)convert_ttf_to_ps, METH_KEYWORDS, |
| 159 | + "convert_ttf_to_ps(filename, output, fonttype, glyph_ids)\n" |
| 160 | + "\n" |
| 161 | + "Converts the Truetype font into a Type 3 or Type 42 Postscript font, " |
| 162 | + "optionally subsetting the font to only the desired set of characters.\n" |
| 163 | + "\n" |
| 164 | + "filename is the path to a TTF font file.\n" |
| 165 | + "output is a Python file-like object with a write method that the Postscript " |
| 166 | + "font data will be written to.\n" |
| 167 | + "fonttype may be either 3 or 42. Type 3 is a \"raw Postscript\" font. " |
| 168 | + "Type 42 is an embedded Truetype font. Glyph subsetting is not supported " |
| 169 | + "for Type 42 fonts.\n" |
| 170 | + "glyph_ids (optional) is a list of glyph ids (integers) to keep when " |
| 171 | + "subsetting to a Type 3 font. If glyph_ids is not provided or is None, " |
| 172 | + "then all glyphs will be included. If any of the glyphs specified are " |
| 173 | + "composite glyphs, then the component glyphs will also be included." |
| 174 | + }, |
| 175 | + {"get_pdf_charprocs", (PyCFunction)py_get_pdf_charprocs, METH_KEYWORDS, |
| 176 | + "get_pdf_charprocs(filename, glyph_ids)\n" |
| 177 | + "\n" |
| 178 | + "Given a Truetype font file, returns a dictionary containing the PDF Type 3\n" |
| 179 | + "representation of its path. Useful for subsetting a Truetype font inside\n" |
| 180 | + "of a PDF file.\n" |
| 181 | + "\n" |
| 182 | + "filename is the path to a TTF font file.\n" |
| 183 | + "glyph_ids is a list of the numeric glyph ids to include.\n" |
| 184 | + "The return value is a dictionary where the keys are glyph names and \n" |
| 185 | + "the values are the stream content needed to render that glyph. This\n" |
| 186 | + "is useful to generate the CharProcs dictionary in a PDF Type 3 font.\n" |
| 187 | + }, |
| 188 | + {NULL} /* Sentinel */ |
| 189 | +}; |
| 190 | + |
| 191 | +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ |
| 192 | +#define PyMODINIT_FUNC void |
177 | 193 | #endif |
178 | | -initttconv(void) |
| 194 | +PyMODINIT_FUNC |
| 195 | +initttconv(void) |
179 | 196 | { |
180 | | - static ttconv_module* ttconv = new ttconv_module; |
| 197 | + PyObject* m; |
| 198 | + |
| 199 | + m = Py_InitModule3("ttconv", ttconv_methods, |
| 200 | + "Module to handle converting and subsetting TrueType " |
| 201 | + "fonts to Postscript Type 3, Postscript Type 42 and " |
| 202 | + "Pdf Type 3 fonts."); |
181 | 203 | } |
| 204 | + |
0 commit comments