1 /*
2 * Copyright 2014 Adobe Systems Incorporated. All rights reserved.
3 * Copyright 2017 Khaled Hosny
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use these files except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #define PY_SSIZE_T_CLEAN 1
19 #include <Python.h>
20
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "psautohint.h"
27
28 static void
reportCB(char * msg,int level)29 reportCB(char* msg, int level)
30 {
31 static PyObject* logger = NULL;
32
33 if (logger == NULL) {
34 PyObject* logging = PyImport_ImportModule("logging");
35 if (logging == NULL)
36 return;
37 logger = PyObject_CallMethod(logging, "getLogger", "s", "_psautohint");
38 if (logger == NULL)
39 return;
40 }
41
42 switch (level) {
43 case AC_LogDebug:
44 PyObject_CallMethod(logger, "debug", "s", msg);
45 break;
46 case AC_LogInfo:
47 PyObject_CallMethod(logger, "info", "s", msg);
48 break;
49 case AC_LogWarning:
50 PyObject_CallMethod(logger, "warning", "s", msg);
51 break;
52 case AC_LogError:
53 PyObject_CallMethod(logger, "error", "s", msg);
54 break;
55 default:
56 break;
57 }
58 }
59
60 static void
charZoneCB(float top,float bottom,char * glyphName,void * userData)61 charZoneCB(float top, float bottom, char* glyphName, void* userData)
62 {
63 ACBufferWriteF((ACBuffer*)userData, "charZone %s top %f bottom %f\n",
64 glyphName, (double)top, (double)bottom);
65 }
66
67 static void
stemZoneCB(float top,float bottom,char * glyphName,void * userData)68 stemZoneCB(float top, float bottom, char* glyphName, void* userData)
69 {
70 ACBufferWriteF((ACBuffer*)userData, "stemZone %s top %f bottom %f\n",
71 glyphName, (double)top, (double)bottom);
72 }
73
74 static void
hstemCB(float top,float bottom,char * glyphName,void * userData)75 hstemCB(float top, float bottom, char* glyphName, void* userData)
76 {
77 ACBufferWriteF((ACBuffer*)userData, "HStem %s top %f bottom %f\n",
78 glyphName, (double)top, (double)bottom);
79 }
80
81 static void
vstemCB(float right,float left,char * glyphName,void * userData)82 vstemCB(float right, float left, char* glyphName, void* userData)
83 {
84 ACBufferWriteF((ACBuffer*)userData, "VStem %s right %f left %f\n",
85 glyphName, (double)right, (double)left);
86 }
87
88 static void
reportRetry(void * userData)89 reportRetry(void* userData)
90 {
91 ACBufferReset((ACBuffer*)userData);
92 }
93
94 static void*
memoryManager(void * ctx,void * ptr,size_t size)95 memoryManager(void* ctx, void* ptr, size_t size)
96 {
97 if (!ptr && !size)
98 return NULL;
99
100 if (ptr && size)
101 ptr = PyMem_RawRealloc(ptr, size);
102 else if (size)
103 ptr = PyMem_RawCalloc(1, size);
104 else
105 PyMem_RawFree(ptr);
106
107 return ptr;
108 }
109
110 static PyObject* PsAutoHintError;
111
112 static char autohint_doc[] =
113 "Autohint glyphs.\n"
114 "\n"
115 "Signature:\n"
116 " autohint(font_info, glyphs[, no_edit, allow_hint_sub, round])\n"
117 "\n"
118 "Args:\n"
119 " font_info: font information.\n"
120 " glyph: glyph data in bez format.\n"
121 " allow_edit: allow editing (changing) the paths when hinting.\n"
122 " allow_hint_sub: no multiple layers of coloring.\n"
123 " round: round coordinates.\n"
124 "\n"
125 "Output:\n"
126 " Autohinted glyph data in bez format.\n"
127 "\n"
128 "Raises:\n"
129 " psautohint.error: If autohinting fails.\n";
130
131 static PyObject*
autohint(PyObject * self,PyObject * args)132 autohint(PyObject* self, PyObject* args)
133 {
134 int allowEdit = true, roundCoords = true, allowHintSub = true;
135 int report = 0, allStems = false;
136 PyObject* fontObj = NULL;
137 PyObject* inObj = NULL;
138 PyObject* outObj = NULL;
139 char* inData = NULL;
140 char* fontInfo = NULL;
141 bool error = true;
142 ACBuffer* reportBuffer = NULL;
143
144 if (!PyArg_ParseTuple(args, "O!O!|iiiii", &PyBytes_Type, &fontObj,
145 &PyBytes_Type, &inObj, &allowEdit, &allowHintSub,
146 &roundCoords, &report, &allStems))
147 return NULL;
148
149 if (report) {
150 reportBuffer = ACBufferNew(150);
151 allowEdit = allowHintSub = false;
152 switch (report) {
153 case 1:
154 AC_SetReportRetryCB(reportRetry, (void*)reportBuffer);
155 AC_SetReportZonesCB(charZoneCB, stemZoneCB,
156 (void*)reportBuffer);
157 break;
158 case 2:
159 AC_SetReportRetryCB(reportRetry, (void*)reportBuffer);
160 AC_SetReportStemsCB(hstemCB, vstemCB, allStems,
161 (void*)reportBuffer);
162 break;
163 default:
164 PyErr_SetString(PyExc_ValueError,
165 "Invalid \"report\" argument, must be 1 or 2");
166 goto done;
167 }
168 }
169
170 AC_SetMemManager(NULL, memoryManager);
171 AC_SetReportCB(reportCB);
172
173 fontInfo = PyBytes_AsString(fontObj);
174 inData = PyBytes_AsString(inObj);
175 if (inData && fontInfo) {
176 int result = -1;
177
178 ACBuffer* output = ACBufferNew(4 * strlen(inData));
179 if (output) {
180 result = AutoHintString(inData, fontInfo, output, allowEdit,
181 allowHintSub, roundCoords);
182
183 if (result == AC_Success) {
184 char* data;
185 size_t len;
186 error = false;
187 if (reportBuffer)
188 ACBufferRead(reportBuffer, &data, &len);
189 else
190 ACBufferRead(output, &data, &len);
191 outObj = PyBytes_FromStringAndSize(data, len);
192 }
193 }
194 ACBufferFree(output);
195 output=NULL;
196
197 if (result != AC_Success) {
198 switch (result) {
199 case -1:
200 /* Do nothing, we already called PyErr_* */
201 break;
202 case AC_FatalError:
203 PyErr_SetString(PsAutoHintError, "Fatal error");
204 break;
205 case AC_InvalidParameterError:
206 PyErr_SetString(PyExc_ValueError, "Invalid glyph data");
207 break;
208 case AC_UnknownError:
209 default:
210 PyErr_SetString(PsAutoHintError, "Hinting failed");
211 break;
212 }
213 }
214 }
215
216 done:
217 ACBufferFree(reportBuffer);
218 reportBuffer = NULL;
219 AC_initCallGlobals(); /* clear out references to reportBuffer */
220
221 if (error)
222 return NULL;
223 return outObj;
224 }
225
226 static char autohintmm_doc[] =
227 "Autohint glyphs.\n"
228 "\n"
229 "Signature:\n"
230 " autohintm(font_info, glyphs)\n"
231 "\n"
232 "Args:\n"
233 " glyphs: sequence of glyph data in bez format.\n"
234 " masters: sequence of master names.\n"
235 "\n"
236 "Output:\n"
237 " Sequence of autohinted glyph data in bez format.\n"
238 "\n"
239 "Raises:\n"
240 " psautohint.error: If autohinting fails.\n";
241
242 static PyObject*
autohintmm(PyObject * self,PyObject * args)243 autohintmm(PyObject* self, PyObject* args)
244 {
245 PyObject* inObj = NULL;
246 Py_ssize_t inCount = 0;
247 PyObject* mastersObj = NULL;
248 Py_ssize_t mastersCount = 0;
249 PyObject* outSeq = NULL;
250 const char** masters;
251 bool error = true;
252 Py_ssize_t i;
253
254 if (!PyArg_ParseTuple(args, "O!O!", &PyTuple_Type, &inObj, &PyTuple_Type,
255 &mastersObj))
256 return NULL;
257
258 inCount = PyTuple_GET_SIZE(inObj);
259 mastersCount = PyTuple_GET_SIZE(mastersObj);
260 if (inCount != mastersCount) {
261 PyErr_SetString(
262 PyExc_TypeError,
263 "Length of \"glyphs\" must equal length of \"masters\".");
264 return NULL;
265 }
266
267 if (inCount <= 1) {
268 PyErr_SetString(PyExc_TypeError, "Length of input glyphs must be > 1");
269 return NULL;
270 }
271
272 masters = PyMem_RawCalloc(mastersCount, sizeof(char*));
273 if (!masters) {
274 PyErr_NoMemory();
275 return NULL;
276 }
277
278 for (i = 0; i < mastersCount; i++) {
279 PyObject* obj = PyTuple_GET_ITEM(mastersObj, i);
280 masters[i] = PyBytes_AsString(obj);
281 if (!masters[i])
282 goto done;
283 }
284
285 AC_SetMemManager(NULL, memoryManager);
286 AC_SetReportCB(reportCB);
287
288 outSeq = PyTuple_New(inCount);
289 if (outSeq) {
290 int result = -1;
291
292 const char** inGlyphs = PyMem_RawCalloc(inCount, sizeof(char*));
293 ACBuffer** outGlyphs = PyMem_RawCalloc(inCount, sizeof(ACBuffer*));
294 if (!inGlyphs || !outGlyphs) {
295 PyErr_NoMemory();
296 goto finish;
297 }
298
299 for (i = 0; i < inCount; i++) {
300 PyObject* glyphObj = PyTuple_GET_ITEM(inObj, i);
301 inGlyphs[i] = PyBytes_AsString(glyphObj);
302 if (!inGlyphs[i])
303 goto finish;
304 outGlyphs[i] = ACBufferNew(4 * strlen(inGlyphs[i]));
305 }
306
307 result = AutoHintStringMM(inGlyphs, mastersCount, masters, outGlyphs);
308 if (result == AC_Success) {
309 error = false;
310 for (i = 0; i < inCount; i++) {
311 PyObject* outObj;
312 char* data;
313 size_t len;
314
315 ACBufferRead(outGlyphs[i], &data, &len);
316 outObj = PyBytes_FromStringAndSize(data, len);
317 PyTuple_SET_ITEM(outSeq, i, outObj);
318 }
319 }
320
321 finish:
322 if (outGlyphs) {
323 for (i = 0; i < inCount; i++) {
324 ACBufferFree(outGlyphs[i]);
325 outGlyphs[i] = NULL;
326 }
327 }
328
329 PyMem_RawFree(inGlyphs);
330 PyMem_RawFree(outGlyphs);
331
332 if (result != AC_Success) {
333 switch (result) {
334 case -1:
335 /* Do nothing, we already called PyErr_* */
336 break;
337 case AC_FatalError:
338 PyErr_SetString(PsAutoHintError, "Fatal error");
339 break;
340 case AC_InvalidParameterError:
341 PyErr_SetString(PyExc_ValueError, "Invalid glyph data");
342 break;
343 case AC_UnknownError:
344 default:
345 PyErr_SetString(PsAutoHintError, "Hinting failed");
346 break;
347 }
348 }
349 }
350
351 done:
352 PyMem_RawFree(masters);
353
354 if (error) {
355 Py_XDECREF(outSeq);
356 return NULL;
357 }
358
359 return outSeq;
360 }
361
362 /* clang-format off */
363 static PyMethodDef psautohint_methods[] = {
364 { "autohint", autohint, METH_VARARGS, autohint_doc },
365 { "autohintmm", autohintmm, METH_VARARGS, autohintmm_doc },
366 { NULL, NULL, 0, NULL }
367 };
368 /* clang-format on */
369
370 static char psautohint_doc[] =
371 "Python wrapper for Adobe's PostScrupt autohinter.\n"
372 "\n"
373 "autohint() -- Autohint glyphs.\n";
374
375 #define SETUPMODULE \
376 PyModule_AddStringConstant(m, "version", AC_getVersion()); \
377 PsAutoHintError = PyErr_NewException("psautohint.error", NULL, NULL); \
378 Py_INCREF(PsAutoHintError); \
379 PyModule_AddObject(m, "error", PsAutoHintError);
380
381 /* clang-format off */
382 static struct PyModuleDef psautohint_module = {
383 PyModuleDef_HEAD_INIT,
384 "_psautohint",
385 psautohint_doc,
386 0,
387 psautohint_methods,
388 NULL,
389 NULL,
390 NULL,
391 NULL
392 };
393 /* clang-format on */
394
395 PyMODINIT_FUNC
PyInit__psautohint(void)396 PyInit__psautohint(void)
397 {
398 PyObject* m;
399
400 m = PyModule_Create(&psautohint_module);
401 if (m == NULL)
402 return NULL;
403
404 SETUPMODULE
405
406 return m;
407 }
408