1 /* Copyright (C) 2007-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 /*			   Python Interface to FontForge		      */
28 
29 #include <fontforge-config.h>
30 #include <fontforge-version-extras.h>
31 
32 #ifndef _NO_PYTHON
33 
34 #include "autohint.h"
35 #include "autotrace.h"
36 #include "autowidth2.h"
37 #include "bitmapcontrol.h"
38 #include "cvexport.h"
39 #include "cvimages.h"
40 #include "cvundoes.h"
41 #include "dumppfa.h"
42 #include "encoding.h"
43 #include "featurefile.h"
44 #include "ffglib.h"
45 #include "ffpython.h"
46 #include "flaglist.h"
47 #include "fontforgevw.h"
48 #include "fvcomposite.h"
49 #include "fvfonts.h"
50 #include "fvimportbdf.h"
51 #include "glyphcomp.h"
52 #include "langfreq.h"
53 #include "lookups.h"
54 #include "mathconstants.h"
55 #include "mem.h"
56 #include "namelist.h"
57 #include "nonlineartrans.h"
58 #include "othersubrs.h"
59 #include "print.h"
60 #include "psread.h"
61 #include "savefont.h"
62 #include "scriptfuncs.h"
63 #include "scripting.h"
64 #include "scstyles.h"
65 #include "search.h"
66 #include "sfd.h"
67 #include "spiro.h"
68 #include "splinefill.h"
69 #include "splineorder2.h"
70 #include "splineoverlap.h"
71 #include "splinesaveafm.h"
72 #include "splinestroke.h"
73 #include "splineutil.h"
74 #include "splineutil2.h"
75 #include "start.h"
76 #include "svg.h"
77 #include "tottf.h"
78 #include "tottfgpos.h"
79 #include "ttf.h"
80 #include "ttfinstrs.h"
81 #include "unicodelibinfo.h"
82 #include "ustring.h"
83 #include "utanvec.h"
84 #include "utype.h"
85 
86 #include <dirent.h>
87 #include <errno.h>
88 #include <math.h>
89 #include <stdarg.h>
90 #include <stdio.h>
91 #include <string.h>
92 #include <sys/stat.h>
93 #include <sys/types.h>
94 #include <unistd.h>
95 #include <wchar.h>
96 
97 extern int old_sfnt_flags;
98 extern int prefRevisionsToRetain;
99 
100 /* ========== MODULE DEFINITIONS ========== */
101 /* The following types are used to define Python Modules.  For every
102  * module, you must create a 'module_definition' structure describing
103  * it.  Then toward the bottom of this file in the "MODULE REGISTRY"
104  * section, make sure you add the module to the list of all modules.
105  */
106 
107 typedef int (*type_initializer)(PyTypeObject *);
108 
109 /* Typedef 'python_type_info' -- Create an array of these, one for
110  * each python class (or type) that your module defines.  End the
111  * array with a sentinel using the TYPEINFO_EMPTY macro
112  */
113 typedef struct {
114     PyTypeObject *typeobj; /* The python type definition. */
115     int add_to_module; /* Include this type's name in the module namespace
116 			* if true, or keep it hidden if false.
117 			*/
118     type_initializer setup_function; /* Called to do any extra modifications or
119 				      * setup of the type definition prior to
120 				      * the finalizing PyTypeReady() call.
121 				      */
122 } python_type_info;
123 #define TYPEINFO_EMPTY { NULL, 0, NULL }  /* Sentinel */
124 
125 
126 /* Typedef 'module_definition' -- Create one of these structures for
127  * each Python module that is being defined.
128  *
129  * Be sure to register the defintion in the "MODULE REGISTRY" section.
130  */
131 typedef struct {
132     /* Set these members as initial constant values. */
133     const char *module_name;
134     const char *docstring;
135     python_type_info *types; /* array of type defintions, or NULL */
136     PyMethodDef *methods; /* array of module-level methods/functions, or NULL */
137     int auto_import;  /* automatically import module in embedded mode? */
138 
139     void (*finalize_func)(PyObject* module); /* Called to do any remaining post-initialization fixups */
140 
141     /* These following members are set at runtime; initialize using
142      * the MODULEDEF_RUNTIMEINFO_INIT macro.
143      */
144     struct {
145 	PyObject *module;
146 	PyObject* (*modinit_func)(void);
147 	PyModuleDef pymod_def;
148     } runtime;
149 } module_definition;
150 
151 #define MODULEDEF_RUNTIMEINFO_INIT { NULL, NULL, {PyModuleDef_HEAD_INIT,NULL,NULL,-1,NULL,NULL,NULL,NULL,NULL} }
152 
153 /* ----------------------------------------------------- */
154 
155 static PyObject *InitializePythonMainNamespace(void);
156 
157 
158 /* Takes an ASCII string and returns a newly-allocated C "wide" string
159  * equivalent.
160  */
copy_to_wide_string(const char * s)161 static wchar_t *copy_to_wide_string(const char *s) {
162     size_t n;
163     wchar_t *ws;
164 
165     ws = NULL;
166     n = mbstowcs(NULL, s, 0) + 1;
167     if (n != (size_t) -1) {
168         ws = calloc(n, sizeof(wchar_t));
169         mbstowcs(ws, s, n);
170     }
171     return ws;
172 }
173 
174 static struct flaglist sfnt_name_str_ids[];
175 static struct flaglist sfnt_name_mslangs[];
176 
177 /*** These have been moved elsewhere ***/
178 /*FontViewBase *fv_active_in_ui = NULL;*/
179 /*SplineChar *sc_active_in_ui = NULL;*/
180 /*int layer_active_in_ui = ly_fore;*/
181 
182 static PyObject *hook_dict;			/* Dictionary of python hook scripts (to be activated when certain fontforge events happen) */
183 static PyObject *pickler, *unpickler;		/* cPickle.dumps, cPickle.loads */
184 static PyObject *_new_point, *_new_contour, *_new_layer;	/* Python handles to c functions, needed for pickler */
185 static void PyFF_PickleTypesInit(void);
186 
187 /* A contour is a list of points, some on curve, some off. */
188 /* A closed contour is a circularly linked list */
189 /* A contour may be either quadratic or cubic */
190 /* cubic contours have two off-curve points between on-curve points -- or none for lines */
191 /* quadratic contours have one off-curve points between on-curve -- or none for lines */
192 /*  quadratic contours may also have two adjacent off-curve points, in which case an on-curve point is interpolated between (as in truetype) */
193 /* A layer is a set of contours all to be drawn together */
194 
195 
196 
197 static int SSSelectOnCurve(SplineSet *ss,int pos);
198 static SplineSet *_SSFromContour(PyFF_Contour *, int *start, int flags);
199 static PyFF_Contour *ContourFromSS(SplineSet *,PyFF_Contour *);
200 static SplineSet *SSFromContour(PyFF_Contour *contour, int *tt_start);
201 static SplineSet *_SSFromLayer(PyFF_Layer *, int flags);
202 static SplineSet *SSFromLayer(PyFF_Layer *layer);
203 static PyFF_Layer *LayerFromSS(SplineSet *ss,PyFF_Layer *layer);
204 static PyFF_Layer *LayerFromLayer(Layer *,PyFF_Layer *);
205 
206 
207 /* ************************************************************************** */
208 /* Find python objects corresponding to non-python structures */
209 /* ************************************************************************** */
PyFF_FontForFV(FontViewBase * fv)210 PyObject* PyFF_FontForFV( FontViewBase *fv ) {
211     if ( fv==NULL )
212 	return NULL;
213     if ( fv->python_fv_object==NULL ) {
214         fv->python_fv_object = PyFF_FontType.tp_alloc(&PyFF_FontType,0);
215         ((PyFF_Font *) (fv->python_fv_object))->fv = fv;
216         Py_INCREF( (PyObject *) (fv->python_fv_object) );
217     }
218     return fv->python_fv_object;
219 }
PyFF_FontForFV_I(FontViewBase * fv)220 PyObject* PyFF_FontForFV_I( FontViewBase *fv ) {
221     PyObject* pyfv = PyFF_FontForFV(fv);
222     Py_XINCREF(pyfv);
223     return pyfv;
224 }
PyFF_FontForSF(SplineFont * sf)225 static PyFF_Font* PyFF_FontForSF( SplineFont *sf ) {
226     if ( sf==NULL )
227 	return NULL;
228     return (PyFF_Font*)PyFF_FontForFV( sf->fv );
229 }
PyFF_FontForSC(SplineChar * sc)230 static PyFF_Font* PyFF_FontForSC( SplineChar *sc ) {
231     if ( sc==NULL )
232 	return NULL;
233     return PyFF_FontForSF( sc->parent );
234 }
PyFF_GlyphForSC(SplineChar * sc)235 static PyFF_Glyph* PyFF_GlyphForSC( SplineChar *sc ) {
236     if ( sc==NULL )
237 	return NULL;
238     return sc->python_sc_object;
239 }
240 
241 /* ************************************************************************** */
242 /* Checks for closed fonts */
243 /* ************************************************************************** */
244 
IsFontClosed(const PyFF_Font * self)245 static int IsFontClosed(const PyFF_Font *self) {
246     return( self==NULL || self->fv==NULL );
247 }
248 
CheckIfFontClosed(const PyFF_Font * self)249 static int CheckIfFontClosed(const PyFF_Font *self) {
250     if ( IsFontClosed(self) ) {
251 	PyErr_Format(PyExc_RuntimeError, "Operation is not allowed after font has been closed" );
252 return( -1 );
253     }
254     return 0;
255 }
256 
257 /* ************************************************************************** */
258 /* Utilities */
259 /* ************************************************************************** */
260 
261 static GPtrArray *default_pyinit_dirs(void);
262 static int dir_exists(const char* path);
263 
264 
FreeStringArray(int cnt,char ** names)265 static void FreeStringArray( int cnt, char **names ) {
266     int i;
267     if ( names==NULL )
268 	return;
269     for ( i=1; i<cnt; ++i ) {
270 	if ( names[i] != NULL )
271 	    free(names[i]);
272     }
273     free(names);
274 }
275 
276 typedef int (*cmpfunc)(PyObject*, PyObject*);
277 
278 /* Wrap a cmpfunc in a richcmpfunc. */
enrichened_compare(cmpfunc compare,PyObject * a,PyObject * b,int op)279 static PyObject *enrichened_compare(cmpfunc compare, PyObject *a, PyObject *b, int op)
280 {
281     PyObject *result = NULL;
282     int cmp;
283 
284     cmp = compare(a, b);
285     if (cmp == -1 && PyErr_Occurred()) {
286         if (PyErr_ExceptionMatches(PyExc_TypeError))
287             switch (op) {
288                 case Py_EQ:
289                     PyErr_Clear();
290                     result = Py_False;
291                     break;
292                 case Py_NE:
293                     PyErr_Clear();
294                     result = Py_True;
295                     break;
296 	        default:
297 		    break;
298             }
299     } else {
300         switch (op) {
301             case Py_LT:
302                 result = (cmp < 0) ? Py_True : Py_False;
303                 break;
304             case Py_LE:
305                 result = (cmp <= 0) ? Py_True : Py_False;
306                 break;
307             case Py_EQ:
308                 result = (cmp == 0) ? Py_True : Py_False;
309                 break;
310             case Py_NE:
311                 result = (cmp != 0) ? Py_True : Py_False;
312                 break;
313             case Py_GT:
314                 result = (cmp > 0) ? Py_True : Py_False;
315                 break;
316             case Py_GE:
317                 result = (cmp >= 0) ? Py_True : Py_False;
318                 break;
319             default:
320                 result = Py_NotImplemented;
321         }
322     }
323     Py_XINCREF(result);
324     return result;
325 }
326 
FlagsFromString(const char * str,struct flaglist * flags,const char * flagkind)327 static int FlagsFromString(const char *str, struct flaglist *flags, const char *flagkind) {
328     int i;
329     i = FindFlagByName( flags, str );
330     if ( i == FLAG_UNKNOWN ) {
331 	if ( flagkind == NULL )
332 	    flagkind = "flag";
333 
334 	PyErr_Format( PyExc_ValueError, "Unknown %s \"%s\"", flagkind, str );
335     }
336 return( i );
337 }
338 
339 /* FlagsFromTuple() - Converts a python sequence of strings into a flags integer.
340  * On an error a python exception is raised and FLAG_UNKNOWN returned.
341  * If flagkind is not NULL, then it is used in any error message to identify
342  * the kind of flags being parsed.
343  */
FlagsFromTuple(PyObject * tuple,struct flaglist * flags,const char * flagkind)344 int FlagsFromTuple(PyObject *tuple,struct flaglist *flags, const char *flagkind) {
345     int ret = 0,temp;
346     int i;
347     const char *str = NULL;
348     PyObject *obj;
349 
350     if ( flagkind == NULL )
351 	flagkind = "flag";
352 
353     /* Might be omitted */
354     if ( tuple == NULL )
355 return( 0 );
356     /* Might just be one string, might be a tuple (or any sequence) of strings */
357     if ( PyUnicode_Check(tuple)) {
358         if ((str = PyUnicode_AsUTF8(tuple)) == NULL) {
359             return FLAG_UNKNOWN;
360         }
361         return FlagsFromString(str,flags,flagkind);
362     } else if ( PySequence_Check(tuple)) {
363 	ret = 0;
364 	for ( i=0; i<PySequence_Size(tuple); ++i ) {
365 	    obj = PySequence_GetItem(tuple,i);
366 	    if ( obj==Py_None )
367 	continue;
368 	    if ( !PyUnicode_Check(obj)) {
369 		PyErr_Format(PyExc_TypeError, "Bad %s list, must consist of strings only", flagkind);
370 return( FLAG_UNKNOWN );
371 	    } else if ((str = PyUnicode_AsUTF8(obj)) == NULL) {
372 		return FLAG_UNKNOWN;
373 	    }
374             temp = FlagsFromString(str,flags,flagkind);
375 	    if ( temp==FLAG_UNKNOWN )
376 return( FLAG_UNKNOWN );
377 	    ret |= temp;
378 	}
379 return( ret );
380     } else {
381 	PyErr_Format(PyExc_TypeError, "Bad %s list, must be a single string or a sequence (tuple/list) of strings", flagkind);
382 return( FLAG_UNKNOWN );
383     }
384 }
385 
PyFF_ValToObject(Val * val)386 static PyObject *PyFF_ValToObject(Val *val) {
387     if ( val->type==v_int || val->type==v_unicode )
388 return( Py_BuildValue("i", val->u.ival ));
389     else if ( val->type==v_str )
390 return( Py_BuildValue("s", val->u.sval ));
391     else if ( val->type==v_real )
392 return( Py_BuildValue("d", val->u.fval ));
393     else if ( val->type==v_arr || val->type==v_arrfree )
394 	PyErr_SetString(PyExc_NotImplementedError, "Array -> tuple conversion not yet implemented. I didn't think I needed to.");
395 return( NULL );
396 }
397 
PySC_From_SC(SplineChar * sc)398 PyObject *PySC_From_SC(SplineChar *sc) {
399     if ( sc->python_sc_object==NULL ) {
400 	sc->python_sc_object = PyFF_GlyphType.tp_alloc(&PyFF_GlyphType,0);
401 	((PyFF_Glyph *) (sc->python_sc_object))->sc = sc;
402 	((PyFF_Glyph *) (sc->python_sc_object))->layer = ly_fore;
403 	Py_INCREF( (PyObject *) (sc->python_sc_object) );	/* for the pointer in my fv */
404     }
405 return( sc->python_sc_object );
406 }
407 
PySC_From_SC_I(SplineChar * sc)408 static PyObject *PySC_From_SC_I(SplineChar *sc) {
409     PyObject *s = PySC_From_SC(sc);
410     Py_INCREF(s);
411 return( s );
412 }
413 
PyFF_Glyph_Set_Layer(SplineChar * sc,int layer)414 void PyFF_Glyph_Set_Layer(SplineChar *sc,int layer) {
415     PyObject *pysc = PySC_From_SC(sc);
416     ((PyFF_Glyph *) pysc)->layer = layer;
417 }
418 
419 #define BAD_TAG ((uint32)0xffffffff)
StrToTag(const char * tag_name,int * was_mac)420 static uint32 StrToTag(const char *tag_name, int *was_mac) {
421     uint8 foo[4];
422     int feat, set;
423 
424     if ( tag_name==NULL ) {
425 	PyErr_Format(PyExc_TypeError, "OpenType tags must be represented as strings" );
426 return( BAD_TAG );
427     }
428 
429     if ( was_mac!=NULL && sscanf(tag_name,"<%d,%d>", &feat, &set )==2 ) {
430 	if ( feat < 0 || set < 0 ) {
431 	    PyErr_Format(PyExc_ValueError, "OpenType tag feature or set number must not be negative: %s", tag_name);
432 return( BAD_TAG );
433 	}
434 	*was_mac = true;
435 return( ( ((uint32)feat)<<16) | set );
436     }
437 
438     if ( was_mac ) *was_mac = false;
439     foo[0] = foo[1] = foo[2] = foo[3] = ' ';
440     if ( *tag_name!='\0' ) {
441 	foo[0] = tag_name[0];
442 	if ( tag_name[1]!='\0' ) {
443 	    foo[1] = tag_name[1];
444 	    if ( tag_name[2]!='\0' ) {
445 		foo[2] = tag_name[2];
446 		if ( tag_name[3]!='\0' ) {
447 		    foo[3] = tag_name[3];
448 		    if ( tag_name[4]!='\0' ) {
449 			PyErr_Format(PyExc_ValueError, "OpenType tags are limited to 4 characters: %s", tag_name);
450 return( BAD_TAG );
451 		    }
452 		}
453 	    }
454 	}
455     }
456 return( (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3] );
457 }
458 
StrObjToTag(PyObject * obj,int * was_mac)459 static uint32 StrObjToTag(PyObject *obj, int *was_mac) {
460     const char *str = PyUnicode_AsUTF8(obj);
461     return str ? StrToTag(str, was_mac) : BAD_TAG;
462 }
463 
TagToPythonString(uint32 tag,int ismac)464 static PyObject *TagToPythonString(uint32 tag,int ismac) {
465     char foo[30];
466 
467     if ( ismac ) {
468 	sprintf( foo,"<%d,%d>", tag>>16, tag&0xffff );
469     } else {
470 	foo[0] = tag>>24;
471 	foo[1] = tag>>16;
472 	foo[2] = tag>>8;
473 	foo[3] = tag;
474 	foo[4] = '\0';
475     }
476 return( PyUnicode_FromString(foo));
477 }
478 
479 /* ************************************************************************** */
480 /* Methods of module FontForge                                                */
481 /* ************************************************************************** */
482 
PyFF_GetPrefs(PyObject * UNUSED (self),PyObject * args)483 static PyObject *PyFF_GetPrefs(PyObject *UNUSED(self), PyObject *args) {
484     const char *prefname;
485     Val val;
486 
487     /* Pref names are ascii so no need to worry about encoding */
488     if ( !PyArg_ParseTuple(args,"s",&prefname) )
489 return( NULL );
490     memset(&val,0,sizeof(val));
491 
492     if ( !GetPrefs((char *) prefname,&val)) {
493 	PyErr_Format(PyExc_NameError, "Unknown preference item in GetPrefs: %s", prefname );
494 return( NULL );
495     }
496 return( PyFF_ValToObject(&val));
497 }
498 
PyFF_SetPrefs(PyObject * UNUSED (self),PyObject * args)499 static PyObject *PyFF_SetPrefs(PyObject *UNUSED(self), PyObject *args) {
500     const char *prefname;
501     Val val;
502     double d;
503 
504     memset(&val,0,sizeof(val));
505     /* Pref names are ascii so no need to worry about encoding */
506     if ( PyArg_ParseTuple(args,"si",&prefname,&val.u.ival) )
507 	val.type = v_int;
508     else {
509 	PyErr_Clear();
510 	if ( PyArg_ParseTuple(args,"ss",&prefname, &val.u.sval) )
511 	    val.type = v_str;
512 	else {
513 	    PyErr_Clear();
514 	    if ( PyArg_ParseTuple(args,"sd",&d) ) {
515 		val.u.fval = d;
516 		val.type = v_real;
517 	    } else
518 return( NULL );
519 	}
520     }
521 
522     bool succeeded = SetPrefs((char *) prefname,&val,NULL);
523     if (val.type == v_str && val.u.sval) {
524         val.u.sval = NULL;
525     }
526 
527     if (!succeeded) {
528 	PyErr_Format(PyExc_NameError, "Unknown preference item in SetPrefs: %s", prefname );
529 return( NULL );
530     }
531 Py_RETURN_NONE;
532 }
533 
PyFF_SavePrefs(PyObject * UNUSED (self),PyObject * UNUSED (args))534 static PyObject *PyFF_SavePrefs(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
535 
536     SavePrefs(false);
537 Py_RETURN_NONE;
538 }
539 
PyFF_LoadPrefs(PyObject * UNUSED (self),PyObject * UNUSED (args))540 static PyObject *PyFF_LoadPrefs(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
541 
542     LoadPrefs();
543 Py_RETURN_NONE;
544 }
545 
PyFF_DefaultOtherSubrs(PyObject * UNUSED (self),PyObject * UNUSED (args))546 static PyObject *PyFF_DefaultOtherSubrs(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
547 
548     DefaultOtherSubrs();
549 Py_RETURN_NONE;
550 }
551 
PyFF_ReadOtherSubrsFile(PyObject * UNUSED (self),PyObject * args)552 static PyObject *PyFF_ReadOtherSubrsFile(PyObject *UNUSED(self), PyObject *args) {
553     const char *filename;
554 
555     /* here we do want the default encoding */
556     if ( !PyArg_ParseTuple(args,"s", &filename) )
557 return( NULL );
558 
559     if ( ReadOtherSubrsFile((char *) filename)<=0 ) {
560         PyErr_Format(PyExc_ImportError, "Could not find OtherSubrs file %s",  filename);
561 return( NULL );
562     }
563 
564 Py_RETURN_NONE;
565 }
566 
PyFF_LoadEncodingFile(PyObject * UNUSED (self),PyObject * args)567 static PyObject *PyFF_LoadEncodingFile(PyObject *UNUSED(self), PyObject *args) {
568     const char *filename;
569     char * encodingname = NULL;
570 
571     /* here we do want the default encoding */
572     if ( !PyArg_ParseTuple(args,"s|s", &filename, &encodingname) )
573 return( NULL );
574 
575 return( Py_BuildValue("s", ParseEncodingFile((char *) filename, encodingname)) );
576 }
577 
PyFF_LoadNamelist(PyObject * UNUSED (self),PyObject * args)578 static PyObject *PyFF_LoadNamelist(PyObject *UNUSED(self), PyObject *args) {
579     const char *filename;
580 
581     /* here we do want the default encoding */
582     if ( !PyArg_ParseTuple(args,"s", &filename) )
583 return( NULL );
584 
585     LoadNamelist((char *) filename);
586 
587 Py_RETURN_NONE;
588 }
589 
PyFF_LoadNamelistDir(PyObject * UNUSED (self),PyObject * args)590 static PyObject *PyFF_LoadNamelistDir(PyObject *UNUSED(self), PyObject *args) {
591     const char *filename;
592 
593     /* here we do want the default encoding */
594     if ( !PyArg_ParseTuple(args,"s", &filename) )
595 return( NULL );
596 
597     LoadNamelistDir((char *) filename);
598 
599 Py_RETURN_NONE;
600 }
601 
PyFF_PreloadCidmap(PyObject * UNUSED (self),PyObject * args)602 static PyObject *PyFF_PreloadCidmap(PyObject *UNUSED(self), PyObject *args) {
603     const char *filename, *reg, *order;
604     int supplement;
605 
606     /* here we do want the default encoding for the filename, the others should be ascii */
607     if ( !PyArg_ParseTuple(args,"sssi", &filename, &reg, &order, &supplement) )
608 return( NULL );
609 
610     LoadMapFromFile((char *) filename, (char *) reg, (char *) order, supplement);
611 
612 Py_RETURN_NONE;
613 }
614 
PyFF_UnicodeFromName(PyObject * UNUSED (self),PyObject * args)615 static PyObject *PyFF_UnicodeFromName(PyObject *UNUSED(self), PyObject *args) {
616     char *name;
617     PyObject *ret;
618 
619     if ( !PyArg_ParseTuple(args,"s", &name ))
620 return( NULL );
621 
622     ret = Py_BuildValue("i", UniFromName((char *) name, ui_none,&custom));
623 return( ret );
624 }
625 
PyFF_NameFromUnicode(PyObject * UNUSED (self),PyObject * args)626 static PyObject *PyFF_NameFromUnicode(PyObject *UNUSED(self), PyObject *args) {
627     char buffer[400], *nlist = NULL;
628     const char *name;
629     int uniinterp, uni;
630     NameList *for_new_glyphs;
631     PyObject *ret = NULL;
632 
633     if ( !PyArg_ParseTuple(args, "i|s", &uni, &nlist) )
634 return( NULL );
635 
636     if ( nlist != NULL ) {
637 	uniinterp = ui_none;
638 	for_new_glyphs = NameListByName( nlist );
639 	if ( for_new_glyphs == NULL ) {
640 	    PyErr_Format(PyExc_EnvironmentError, "Unknown namelist: %s\n", nlist);
641 return( NULL );
642 	}
643     } else if (fv_active_in_ui == NULL) {
644 	uniinterp = ui_none;
645 	for_new_glyphs = NameListByName("AGL with PUA");
646     } else {
647 	uniinterp = fv_active_in_ui->sf->uni_interp;
648 	for_new_glyphs = fv_active_in_ui->sf->for_new_glyphs;
649     }
650 
651     name = StdGlyphNameBoundsCheck(buffer,uni,uniinterp,for_new_glyphs);
652     if ( name!=NULL )
653 	ret = Py_BuildValue("s", name);
654     else
655 	PyErr_Format(PyExc_ValueError, "Value %d has no Unicode name.", uni);
656     return( ret );
657 }
658 
PyFF_UnicodeAnnotationFromLib(PyObject * UNUSED (self),PyObject * args)659 static PyObject *PyFF_UnicodeAnnotationFromLib(PyObject *UNUSED(self), PyObject *args) {
660 /* If the library is available, then get the official annotation for this unicode value */
661 /* This function may be used in conjunction with UnicodeNameFromLib(n) */
662     PyObject *ret;
663     char *temp;
664     long val;
665 
666     if ( !PyArg_ParseTuple(args,"|l",&val) )
667 	return( NULL );
668 
669     if ( (temp=unicode_annot(val))==NULL ) {
670 	temp=malloc(1*sizeof(char)); *temp='\0';
671     }
672     ret=Py_BuildValue("s",temp); free(temp);
673     return( ret );
674 }
675 
PyFF_UnicodeNameFromLib(PyObject * UNUSED (self),PyObject * args)676 static PyObject *PyFF_UnicodeNameFromLib(PyObject *UNUSED(self), PyObject *args) {
677 /* If the library is available, then get the official name for this unicode value */
678 /* This function may be used in conjunction with UnicodeAnnotationFromLib(n) */
679     PyObject *ret;
680     char *temp;
681     long val;
682 
683     if ( !PyArg_ParseTuple(args,"|l",&val) )
684 	return( NULL );
685 
686     if ( (temp=unicode_name(val))==NULL ) {
687 	temp=malloc(1*sizeof(char)); *temp='\0';
688     }
689     ret=Py_BuildValue("s",temp); free(temp);
690     return( ret );
691 }
692 
PyFF_UnicodeBlockCountFromLib(PyObject * UNUSED (self),PyObject * UNUSED (args))693 static PyObject *PyFF_UnicodeBlockCountFromLib(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
694 /* If the library is available, then return the number of name blocks */
695 
696     return( Py_BuildValue("i", unicode_block_count()) );
697 }
698 
PyFF_UnicodeBlockStartFromLib(PyObject * UNUSED (self),PyObject * args)699 static PyObject *PyFF_UnicodeBlockStartFromLib(PyObject *UNUSED(self), PyObject *args) {
700 /* If the library is available, then get the official start for this unicode block */
701 /* Use this function with UnicodeBlockNameFromLib(n) & UnicodeBlockEndFromLib(n). */
702     long val;
703 
704     if ( !PyArg_ParseTuple(args,"|l",&val) )
705 	return( NULL );
706 
707     return( Py_BuildValue("l", (long)unicode_block_start(val)) );
708 }
709 
PyFF_UnicodeBlockEndFromLib(PyObject * UNUSED (self),PyObject * args)710 static PyObject *PyFF_UnicodeBlockEndFromLib(PyObject *UNUSED(self), PyObject *args) {
711 /* If the library is available, then get the official end for this unicode block. */
712 /* Use this function with UnicodeBlockStartFromLib(n), UnicodeBlockNameFromLib(n) */
713     long val;
714 
715     if ( !PyArg_ParseTuple(args,"|l",&val) )
716 	return( NULL );
717 
718     return( Py_BuildValue("l", (long)unicode_block_end(val)) );
719 }
720 
PyFF_UnicodeBlockNameFromLib(PyObject * UNUSED (self),PyObject * args)721 static PyObject *PyFF_UnicodeBlockNameFromLib(PyObject *UNUSED(self), PyObject *args) {
722 /* If the library is available, then get the official name for this unicode block */
723 /* Use this function with UnicodeBlockStartFromLib(n), UnicodeBlockEndFromLib(n). */
724     PyObject *ret;
725     char *temp;
726     long val;
727 
728     if ( !PyArg_ParseTuple(args,"|l",&val) )
729 	return( NULL );
730 
731     if ( (temp=unicode_block_name(val))==NULL ) {
732 	temp=malloc(1*sizeof(char)); *temp='\0';
733     }
734     ret=Py_BuildValue("s",temp); free(temp);
735     return( ret );
736 }
737 
PyFF_UnicodeNamesListVersion(PyObject * UNUSED (self),PyObject * UNUSED (args))738 static PyObject *PyFF_UnicodeNamesListVersion(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
739 /* If the library is available, then return the Nameslist Version number */
740     char *temp;
741 
742     if ( (temp=unicode_library_version())==NULL ) {
743 	temp=malloc(1*sizeof(char)); *temp='\0';
744     }
745     PyObject *ret=Py_BuildValue("s",temp); free(temp);
746     return( ret );
747 }
748 
749 /* Names2 lookup commands to get info from attached libuninameslist >= v0.5. */
750 /* Cnt returns lookup table-size, Nxt returns next Unicode value from array, */
751 /* Nxt returns 'n' for table array[0..n..(Cnt-1)] pointer. Errors return -1. */
PyFF_UnicodeNames2GetCntFromLib(PyObject * UNUSED (self),PyObject * UNUSED (args))752 static PyObject *PyFF_UnicodeNames2GetCntFromLib(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
753     return( Py_BuildValue("i", unicode_names2cnt()) );
754 }
755 
PyFF_UnicodeNames2GetNxtFromLib(PyObject * UNUSED (self),PyObject * args)756 static PyObject *PyFF_UnicodeNames2GetNxtFromLib(PyObject *UNUSED(self), PyObject *args) {
757     long val;
758 
759     if ( !PyArg_ParseTuple(args,"|l",&val) )
760 	return( NULL );
761     return( Py_BuildValue("l", (long)unicode_names2getUtabLoc(val)) );
762 }
763 
PyFF_UnicodeNames2NxtUniFromLib(PyObject * UNUSED (self),PyObject * args)764 static PyObject *PyFF_UnicodeNames2NxtUniFromLib(PyObject *UNUSED(self), PyObject *args) {
765     long val;
766 
767     if ( !PyArg_ParseTuple(args,"|l",&val) )
768 	return( NULL );
769     return( Py_BuildValue("l", (long)unicode_names2valFrmTab(val)) );
770 }
771 
PyFF_UnicodeNames2FrmTabFromLib(PyObject * UNUSED (self),PyObject * args)772 static PyObject *PyFF_UnicodeNames2FrmTabFromLib(PyObject *UNUSED(self), PyObject *args) {
773     long val;
774     char *temp;
775 
776     if ( !PyArg_ParseTuple(args,"|l",&val) )
777 	return( NULL );
778     if ( (temp=unicode_name2FrmTab(val))==NULL ) {
779 	return Py_BuildValue("s", "");
780     }
781     PyObject *ret=Py_BuildValue("s",temp); free(temp);
782     return( ret );
783 }
784 
PyFF_UnicodeNames2FromLib(PyObject * UNUSED (self),PyObject * args)785 static PyObject *PyFF_UnicodeNames2FromLib(PyObject *UNUSED(self), PyObject *args) {
786     long val;
787     char *temp;
788 
789     if ( !PyArg_ParseTuple(args,"|l",&val) )
790 	return( NULL );
791     if ( (temp=unicode_name2(val))==NULL ) {
792 	return Py_BuildValue("s", "");
793     }
794     PyObject *ret=Py_BuildValue("s",temp); free(temp);
795     return( ret );
796 }
797 
798 
799 /* Ligature & Fraction information based on current Unicode (builtin) chart. */
800 /* Unicode chart seems to distinguish vulgar fractions from other fractions. */
801 /* These routines test value with internal table. Returns true/false values. */
PyFF_isligature(PyObject * UNUSED (self),PyObject * args)802 static PyObject *PyFF_isligature(PyObject *UNUSED(self), PyObject *args) {
803     long codepoint;
804 
805     if ( !PyArg_ParseTuple(args,"|l",&codepoint) )
806 	return( NULL );
807 
808     return( Py_BuildValue("i", is_LIGATURE(codepoint)==0?1:0) );
809 }
810 
PyFF_isvulgarfraction(PyObject * UNUSED (self),PyObject * args)811 static PyObject *PyFF_isvulgarfraction(PyObject *UNUSED(self), PyObject *args) {
812     long codepoint;
813 
814     if ( !PyArg_ParseTuple(args,"|l",&codepoint) )
815 	return( NULL );
816 
817     return( Py_BuildValue("i", is_VULGAR_FRACTION(codepoint)==0?1:0) );
818 }
819 
PyFF_isotherfraction(PyObject * UNUSED (self),PyObject * args)820 static PyObject *PyFF_isotherfraction(PyObject *UNUSED(self), PyObject *args) {
821     long codepoint;
822 
823     if ( !PyArg_ParseTuple(args,"|l",&codepoint) )
824 	return( NULL );
825 
826     return( Py_BuildValue("i", is_OTHER_FRACTION(codepoint)==0?1:0) );
827 }
828 
PyFF_isfraction(PyObject * UNUSED (self),PyObject * args)829 static PyObject *PyFF_isfraction(PyObject *UNUSED(self), PyObject *args) {
830     long codepoint;
831 
832     if ( !PyArg_ParseTuple(args,"|l",&codepoint) )
833 	return( NULL );
834 
835     return( Py_BuildValue("i", (is_VULGAR_FRACTION(codepoint)==0 || \
836 				is_OTHER_FRACTION(codepoint)==0)?1:0) );
837 }
838 
839 /* Cnt returns lookup table-size, Nxt returns next Unicode value from array, */
840 /* Loc returns 'n' for table array[0..n..(Cnt-1)] pointer. Errors return -1. */
PyFF_LigChartGetCnt(PyObject * UNUSED (self),PyObject * UNUSED (args))841 static PyObject *PyFF_LigChartGetCnt(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
842     return( Py_BuildValue("i", LigatureCount()) );
843 }
844 
PyFF_VulChartGetCnt(PyObject * UNUSED (self),PyObject * UNUSED (args))845 static PyObject *PyFF_VulChartGetCnt(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
846     return( Py_BuildValue("i", VulgarFractionCount()) );
847 }
848 
PyFF_OFracChartGetCnt(PyObject * UNUSED (self),PyObject * UNUSED (args))849 static PyObject *PyFF_OFracChartGetCnt(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
850     return( Py_BuildValue("i", OtherFractionCount()) );
851 }
852 
PyFF_FracChartGetCnt(PyObject * UNUSED (self),PyObject * UNUSED (args))853 static PyObject *PyFF_FracChartGetCnt(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
854     return( Py_BuildValue("i", FractionCount()) );
855 }
856 
857 /* These routines return builtin table unicode values for n in {0..(Cnt-1)}. */
858 /* Internal table array size and values will depend on which unicodelist was */
859 /* used at time makeutype was run to make FontForge's internal utype tables. */
PyFF_LigChartGetNxt(PyObject * UNUSED (self),PyObject * args)860 static PyObject *PyFF_LigChartGetNxt(PyObject *UNUSED(self), PyObject *args) {
861     long val;
862 
863     if ( !PyArg_ParseTuple(args,"|l",&val) )
864 	return( NULL );
865 
866     return( Py_BuildValue("l", (long)Ligature_get_U(val)) );
867 }
868 
PyFF_VulChartGetNxt(PyObject * UNUSED (self),PyObject * args)869 static PyObject *PyFF_VulChartGetNxt(PyObject *UNUSED(self), PyObject *args) {
870     long val;
871 
872     if ( !PyArg_ParseTuple(args,"|l",&val) )
873 	return( NULL );
874 
875     return( Py_BuildValue("l", (long)VulgFrac_get_U(val)) );
876 }
877 
PyFF_OFracChartGetNxt(PyObject * UNUSED (self),PyObject * args)878 static PyObject *PyFF_OFracChartGetNxt(PyObject *UNUSED(self), PyObject *args) {
879     long val;
880 
881     if ( !PyArg_ParseTuple(args,"|l",&val) )
882 	return( NULL );
883 
884     return( Py_BuildValue("l", (long)Fraction_get_U(val)) );
885 }
886 
887 /* If you have a unicode ligature, or fraction, these routines return loc n. */
888 /* Internal table array size and values will depend on which unicodelist was */
889 /* used at time makeutype was run to make FontForge's internal utype tables. */
PyFF_LigChartGetLoc(PyObject * UNUSED (self),PyObject * args)890 static PyObject *PyFF_LigChartGetLoc(PyObject *UNUSED(self), PyObject *args) {
891     long codepoint;
892 
893     if ( !PyArg_ParseTuple(args,"|l",&codepoint) )
894 	return( NULL );
895 
896     return( Py_BuildValue("i", Ligature_find_N(codepoint)) );
897 }
898 
PyFF_VulChartGetLoc(PyObject * UNUSED (self),PyObject * args)899 static PyObject *PyFF_VulChartGetLoc(PyObject *UNUSED(self), PyObject *args) {
900     long codepoint;
901 
902     if ( !PyArg_ParseTuple(args,"|l",&codepoint) )
903 	return( NULL );
904 
905     return( Py_BuildValue("i", VulgFrac_find_N(codepoint)) );
906 }
907 
PyFF_OFracChartGetLoc(PyObject * UNUSED (self),PyObject * args)908 static PyObject *PyFF_OFracChartGetLoc(PyObject *UNUSED(self), PyObject *args) {
909     long codepoint;
910 
911     if ( !PyArg_ParseTuple(args,"|l",&codepoint) )
912 	return( NULL );
913 
914     return( Py_BuildValue("i", Fraction_find_N(codepoint)) );
915 }
916 
917 /* If you have a unicode ligature, or fraction, these routines return alt c. */
PyFF_LigChartGetAltCnt(PyObject * UNUSED (self),PyObject * args)918 static PyObject *PyFF_LigChartGetAltCnt(PyObject *UNUSED(self), PyObject *args) {
919     long codepoint;
920 
921     if ( !PyArg_ParseTuple(args,"l",&codepoint) )
922 	return( NULL );
923     return( Py_BuildValue("i", Ligature_alt_getC(codepoint)) );
924 }
925 
PyFF_LigChartUGetAltCnt(PyObject * UNUSED (self),PyObject * args)926 static PyObject *PyFF_LigChartUGetAltCnt(PyObject *UNUSED(self), PyObject *args) {
927     long codepoint;
928 
929     if ( !PyArg_ParseTuple(args,"l",&codepoint) )
930 	return( NULL );
931     return( Py_BuildValue("i", LigatureU_alt_getC(codepoint)) );
932 }
933 
PyFF_VulChartGetAltCnt(PyObject * UNUSED (self),PyObject * args)934 static PyObject *PyFF_VulChartGetAltCnt(PyObject *UNUSED(self), PyObject *args) {
935     long codepoint;
936 
937     if ( !PyArg_ParseTuple(args,"l",&codepoint) )
938 	return( NULL );
939     return( Py_BuildValue("i", VulgFrac_alt_getC(codepoint)) );
940 }
941 
PyFF_VulChartUGetAltCnt(PyObject * UNUSED (self),PyObject * args)942 static PyObject *PyFF_VulChartUGetAltCnt(PyObject *UNUSED(self), PyObject *args) {
943     long codepoint;
944 
945     if ( !PyArg_ParseTuple(args,"l",&codepoint) )
946 	return( NULL );
947     return( Py_BuildValue("i", VulgFracU_alt_getC(codepoint)) );
948 }
949 
PyFF_OFracChartGetAltCnt(PyObject * UNUSED (self),PyObject * args)950 static PyObject *PyFF_OFracChartGetAltCnt(PyObject *UNUSED(self), PyObject *args) {
951     long codepoint;
952 
953     if ( !PyArg_ParseTuple(args,"l",&codepoint) )
954 	return( NULL );
955     return( Py_BuildValue("i", Fraction_alt_getC(codepoint)) );
956 }
957 
PyFF_OFracChartUGetAltCnt(PyObject * UNUSED (self),PyObject * args)958 static PyObject *PyFF_OFracChartUGetAltCnt(PyObject *UNUSED(self), PyObject *args) {
959     long codepoint;
960 
961     if ( !PyArg_ParseTuple(args,"l",&codepoint) )
962 	return( NULL );
963     return( Py_BuildValue("i", FractionU_alt_getC(codepoint)) );
964 }
965 
966 /* If you have a unicode ligature, or fraction, these routines return alt v. */
PyFF_LigChartGetAltVal(PyObject * UNUSED (self),PyObject * args)967 static PyObject *PyFF_LigChartGetAltVal(PyObject *UNUSED(self), PyObject *args) {
968     long nthCode,altN;
969 
970     if ( !PyArg_ParseTuple(args,"ll",&nthCode, &altN) )
971 	return( NULL );
972     return( Py_BuildValue("l", (long)Ligature_alt_getV(nthCode,altN)) );
973 }
974 
PyFF_LigChartUGetAltVal(PyObject * UNUSED (self),PyObject * args)975 static PyObject *PyFF_LigChartUGetAltVal(PyObject *UNUSED(self), PyObject *args) {
976     long nthCode,altN;
977 
978     if ( !PyArg_ParseTuple(args,"ll",&nthCode, &altN) )
979 	return( NULL );
980     return( Py_BuildValue("l", (long)LigatureU_alt_getV(nthCode,altN)) );
981 }
982 
PyFF_VulChartGetAltVal(PyObject * UNUSED (self),PyObject * args)983 static PyObject *PyFF_VulChartGetAltVal(PyObject *UNUSED(self), PyObject *args) {
984     long nthCode,altN;
985 
986     if ( !PyArg_ParseTuple(args,"ll",&nthCode, &altN) )
987 	return( NULL );
988     return( Py_BuildValue("l", (long)VulgFrac_alt_getV(nthCode,altN)) );
989 }
990 
PyFF_VulChartUGetAltVal(PyObject * UNUSED (self),PyObject * args)991 static PyObject *PyFF_VulChartUGetAltVal(PyObject *UNUSED(self), PyObject *args) {
992     long nthCode,altN;
993 
994     if ( !PyArg_ParseTuple(args,"ll",&nthCode, &altN) )
995 	return( NULL );
996     return( Py_BuildValue("l", (long)VulgFracU_alt_getV(nthCode,altN)) );
997 }
998 
PyFF_OFracChartGetAltVal(PyObject * UNUSED (self),PyObject * args)999 static PyObject *PyFF_OFracChartGetAltVal(PyObject *UNUSED(self), PyObject *args) {
1000     long nthCode,altN;
1001 
1002     if ( !PyArg_ParseTuple(args,"ll",&nthCode, &altN) )
1003 	return( NULL );
1004     return( Py_BuildValue("l", (long)Fraction_alt_getV(nthCode,altN)) );
1005 }
1006 
PyFF_OFracChartUGetAltVal(PyObject * UNUSED (self),PyObject * args)1007 static PyObject *PyFF_OFracChartUGetAltVal(PyObject *UNUSED(self), PyObject *args) {
1008     long nthCode,altN;
1009 
1010     if ( !PyArg_ParseTuple(args,"ll",&nthCode, &altN) )
1011 	return( NULL );
1012     return( Py_BuildValue("l", (long)FractionU_alt_getV(nthCode,altN)) );
1013 }
1014 
PyFF_Version(PyObject * UNUSED (self),PyObject * UNUSED (args))1015 static PyObject *PyFF_Version(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1016 return( Py_BuildValue("s", FONTFORGE_VERSION ));
1017 }
1018 
1019 
PyFF_RunInitScripts(PyObject * UNUSED (self),PyObject * UNUSED (args))1020 static PyObject *PyFF_RunInitScripts(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1021     InitializePythonMainNamespace();
1022     PyFF_ProcessInitFiles();
1023 Py_RETURN_NONE;
1024 }
1025 
PyFF_GetScriptPath(PyObject * UNUSED (self),PyObject * UNUSED (args))1026 static PyObject *PyFF_GetScriptPath(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1027     PyObject *ret;
1028     GPtrArray *dpath;
1029     int cnt;
1030 
1031     dpath = default_pyinit_dirs();
1032     ret = PyTuple_New((int)dpath->len);
1033     for (guint i = 0; i < dpath->len; ++i) {
1034         PyTuple_SET_ITEM(ret, (int)i, Py_BuildValue("s", dpath->pdata[i]));
1035     }
1036 
1037     g_ptr_array_free(dpath, true);
1038 
1039     return ret;
1040 }
1041 
PyFF_GetUserConfigPath(PyObject * UNUSED (self),PyObject * UNUSED (args))1042 static PyObject *PyFF_GetUserConfigPath(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1043     PyObject *ret;
1044     char *userdir;
1045 
1046     userdir = getFontForgeUserDir(Config);
1047     ret=Py_BuildValue("s",userdir);
1048     free(userdir);
1049 
1050     return ret;
1051 }
1052 
PyFF_FontTuple(PyObject * UNUSED (self),PyObject * UNUSED (args))1053 static PyObject *PyFF_FontTuple(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1054     FontViewBase *fv;
1055     int cnt;
1056     PyObject *tuple;
1057 
1058     for ( fv=FontViewFirst(), cnt=0; fv!=NULL; fv=fv->next, ++cnt );
1059     tuple = PyTuple_New(cnt);
1060     for ( fv=FontViewFirst(), cnt=0; fv!=NULL; fv=fv->next, ++cnt )
1061 	PyTuple_SET_ITEM(tuple,cnt,PyFF_FontForFV_I(fv));
1062 
1063 return( tuple );
1064 }
1065 
PyFF_ActiveFont(PyObject * UNUSED (self),PyObject * UNUSED (args))1066 static PyObject *PyFF_ActiveFont(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1067 
1068     if ( fv_active_in_ui==NULL )
1069 Py_RETURN_NONE;
1070 
1071 return( PyFF_FontForFV_I( fv_active_in_ui ));
1072 }
1073 
PyFF_ActiveGlyph(PyObject * UNUSED (self),PyObject * UNUSED (args))1074 static PyObject *PyFF_ActiveGlyph(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1075 
1076     if ( sc_active_in_ui==NULL )
1077 Py_RETURN_NONE;
1078 
1079 return( PySC_From_SC_I( sc_active_in_ui ));
1080 }
1081 
PyFF_ActiveLayer(PyObject * UNUSED (self),PyObject * UNUSED (args))1082 static PyObject *PyFF_ActiveLayer(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1083 
1084 return( Py_BuildValue("i", layer_active_in_ui ));
1085 }
1086 
SFAdd(SplineFont * sf,int hide)1087 static FontViewBase *SFAdd(SplineFont *sf,int hide) {
1088     if ( sf->fv!=NULL )
1089 	/* All done */;
1090     else if ( !no_windowing_ui )
1091 	FontViewCreate(sf,hide);
1092     else
1093 	FVAppend(_FontViewCreate(sf));
1094 return( sf->fv );
1095 }
1096 
1097 
1098 struct flaglist openflaglist[] = {
1099     { "fstypepermitted", of_fstypepermitted },
1100     { "allglyphsinttc", of_all_glyphs_in_ttc },
1101     { "fontlint", of_fontlint },
1102     { "hidewindow", of_hidewindow },
1103     { "alltables", of_all_tables },
1104     FLAGLIST_EMPTY
1105 };
1106 
PyFF_OpenFont(PyObject * UNUSED (self),PyObject * args)1107 static PyObject *PyFF_OpenFont(PyObject *UNUSED(self), PyObject *args) {
1108     char *filename, *locfilename;
1109     int openflags = 0;
1110     SplineFont *sf;
1111     PyObject *flagsobj = NULL;
1112 
1113     if ( !PyArg_ParseTuple(args,"s|O", &filename, &flagsobj ))
1114 	return NULL;
1115     locfilename = utf82def_copy(filename);
1116 
1117     if ( flagsobj!=NULL && PyLong_Check(flagsobj) ) {
1118 	openflags = PyLong_AsLong(flagsobj);
1119     } else if ( flagsobj!=NULL && PyTuple_Check(flagsobj) ) {
1120 	openflags = FlagsFromTuple(flagsobj, openflaglist, "open flag");
1121     } else if ( flagsobj!=NULL ) {
1122 	free(locfilename);
1123 	PyErr_Format(PyExc_IndexError, "Flags must be specified as String Tuple or Int");
1124 	return NULL;
1125     }
1126     /* The actual filename opened may be different from the one passed
1127      * to LoadSplineFont, so we can't report the filename on an
1128      * error.
1129      */
1130     sf = LoadSplineFont(locfilename,openflags);
1131 
1132     if ( sf==NULL ) {
1133 	PyErr_Format(PyExc_EnvironmentError, "Open failed");
1134 	free(locfilename);
1135 return( NULL );
1136     }
1137     free(locfilename);
1138 return( PyFF_FontForFV_I( SFAdd( sf, openflags&of_hidewindow )));
1139 }
1140 
PyFF_FontsInFile(PyObject * UNUSED (self),PyObject * args)1141 static PyObject *PyFF_FontsInFile(PyObject *UNUSED(self), PyObject *args) {
1142     char *filename;
1143     char *locfilename = NULL;
1144     PyObject *tuple;
1145     char **ret;
1146     int cnt, i;
1147 
1148     if ( !PyArg_ParseTuple(args,"s",&filename) )
1149 return( NULL );
1150     locfilename = utf82def_copy(filename);
1151     ret = GetFontNames(locfilename, 1);
1152     free(locfilename);
1153     cnt = 0;
1154     if ( ret!=NULL ) for ( cnt=0; ret[cnt]!=NULL; ++cnt );
1155 
1156     tuple = PyTuple_New(cnt);
1157     for ( i=0; i<cnt; ++i )
1158 	PyTuple_SetItem(tuple,i,Py_BuildValue("s",ret[i]));
1159 return( tuple );
1160 }
1161 
prterror(void * UNUSED (foo),char * msg,int UNUSED (pos))1162 static void prterror(void *UNUSED(foo), char *msg, int UNUSED(pos)) {
1163     fprintf( stderr, "%s\n", msg );
1164 }
1165 
PyFF_ParseTTFInstrs(PyObject * UNUSED (self),PyObject * args)1166 static PyObject *PyFF_ParseTTFInstrs(PyObject *UNUSED(self), PyObject *args) {
1167     PyObject *binstr;
1168     char *instr_str;
1169     int icnt;
1170     uint8 *instrs;
1171 
1172     if ( !PyArg_ParseTuple(args,"s",&instr_str) )
1173 return( NULL );
1174     instrs = _IVParse(NULL,instr_str,&icnt,prterror,NULL);
1175     if ( instrs==NULL ) {
1176 	PyErr_Format(PyExc_TypeError, "Failed to parse instructions" );
1177 return( NULL );
1178     }
1179     binstr = PyBytes_FromStringAndSize((char *) instrs,icnt);
1180     free(instrs);
1181 
1182 return( binstr );
1183 }
1184 
PyFF_UnParseTTFInstrs(PyObject * UNUSED (self),PyObject * args)1185 static PyObject *PyFF_UnParseTTFInstrs(PyObject *UNUSED(self), PyObject *args) {
1186     PyObject *tuple, *ret;
1187     int icnt, i;
1188     uint8 *instrs;
1189     char *as_str;
1190 
1191     if ( !PyArg_ParseTuple(args,"O",&tuple) )
1192 return( NULL );
1193     if ( !PySequence_Check(tuple)) {
1194 	PyErr_Format(PyExc_TypeError, "Argument must be a sequence" );
1195 return( NULL );
1196     }
1197     if ( PyBytes_Check(tuple)) {
1198 	char *space; Py_ssize_t len;
1199 	PyBytes_AsStringAndSize(tuple,&space,&len);
1200 	instrs = calloc(len,sizeof(uint8));
1201 	icnt = len;
1202 	memcpy(instrs,space,len);
1203     } else {
1204 	icnt = PySequence_Size(tuple);
1205 	instrs = malloc(icnt);
1206 	for ( i=0; i<icnt; ++i ) {
1207 	    instrs[i] = PyLong_AsLong(PySequence_GetItem(tuple,i));
1208 	    if ( PyErr_Occurred()) {
1209 		free(instrs);
1210 return( NULL );
1211 	    }
1212 	}
1213     }
1214     as_str = _IVUnParseInstrs(instrs,icnt);
1215     ret = PyBytes_FromString(as_str);
1216     free(as_str); free(instrs);
1217 return( ret );
1218 }
1219 
PyFF_unitShape(PyObject * UNUSED (self),PyObject * args)1220 static PyObject *PyFF_unitShape(PyObject *UNUSED(self), PyObject *args) {
1221     int n=0;
1222     SplineSet *ss;
1223     PyObject *ret;
1224 
1225     if ( !PyArg_ParseTuple(args,"|i",&n) )
1226 return( NULL );
1227     ss = UnitShape(n);
1228     ret = (PyObject *) ContourFromSS(ss,NULL);
1229     SplinePointListFree(ss);
1230 return( ret );
1231 }
1232 
1233 static struct flaglist printmethod[] = {
1234     { "lp", 0 },
1235     { "lpr", 1 },
1236     { "ghostview", 2 },
1237     { "ps-file", 3 },
1238     { "command", 4 },
1239     { "pdf-file", 5 },
1240     FLAGLIST_EMPTY /* Sentinel */
1241 };
1242 
PyFF_printSetup(PyObject * UNUSED (self),PyObject * args)1243 static PyObject *PyFF_printSetup(PyObject *UNUSED(self), PyObject *args) {
1244     char *ptype, *pcmd = NULL;
1245     int iptype;
1246 
1247     if ( !PyArg_ParseTuple(args,"s|sii", &ptype, &pcmd, &pagewidth, &pageheight ) )
1248 return( NULL );
1249     iptype = FlagsFromString(ptype,printmethod,"printing method");
1250     if ( iptype==FLAG_UNKNOWN ) {
1251 return( NULL );
1252     }
1253 
1254     printtype = iptype;
1255     if ( pcmd!=NULL && iptype==4 )
1256 	printcommand = copy(pcmd);
1257     else if ( pcmd!=NULL && (iptype==0 || iptype==1) )
1258 	printlazyprinter = copy(pcmd);
1259 Py_RETURN_NONE;
1260 }
1261 
1262 /* ************************************************************************** */
1263 /* ************************* Import/Export routines ************************* */
1264 /* ************************************************************************** */
1265 
1266 struct python_import_export *py_ie;
1267 static int ie_cnt, ie_max;
1268 
PyFF_SCImport(SplineChar * sc,int ie_index,char * filename,int layer,int clear)1269 void PyFF_SCImport(SplineChar *sc,int ie_index,char *filename,
1270 	int layer, int clear) {
1271     int toback = layer!=ly_fore;
1272     PyObject *arglist, *result, *glyph = PySC_From_SC(sc);
1273 
1274     if ( ie_index>=ie_cnt )
1275 return;
1276     SCPreserveLayer(sc,layer,false);
1277     if ( clear ) {
1278 	SplinePointListsFree(sc->layers[layer].splines);
1279 	sc->layers[layer].splines = NULL;
1280     }
1281 
1282     sc_active_in_ui = sc;
1283     layer_active_in_ui = layer;
1284 
1285     arglist = PyTuple_New(4);
1286     Py_XINCREF(py_ie[ie_index].data);
1287     Py_XINCREF(glyph);
1288     PyTuple_SetItem(arglist,0,py_ie[ie_index].data);
1289     PyTuple_SetItem(arglist,1,glyph);
1290     PyTuple_SetItem(arglist,2,PyUnicode_DecodeUTF8(filename,strlen(filename),NULL));
1291     PyTuple_SetItem(arglist,3,Py_BuildValue("i",toback));
1292     result = PyEval_CallObject(py_ie[ie_index].import, arglist);
1293     Py_DECREF(arglist);
1294     Py_XDECREF(result);
1295     if ( PyErr_Occurred()!=NULL )
1296 	PyErr_Print();
1297 }
1298 
PyFF_SCExport(SplineChar * sc,int ie_index,char * filename,int layer)1299 void PyFF_SCExport(SplineChar *sc,int ie_index,char *filename,int layer) {
1300     PyObject *arglist, *result, *glyph = PySC_From_SC(sc);
1301 
1302     if ( ie_index>=ie_cnt )
1303 return;
1304 
1305     sc_active_in_ui = sc;
1306     layer_active_in_ui = layer;
1307 
1308     arglist = PyTuple_New(3);
1309     Py_XINCREF(py_ie[ie_index].data);
1310     Py_XINCREF(glyph);
1311     PyTuple_SetItem(arglist,0,py_ie[ie_index].data);
1312     PyTuple_SetItem(arglist,1,glyph);
1313     PyTuple_SetItem(arglist,2,PyUnicode_DecodeUTF8(filename,strlen(filename),NULL));
1314     PyTuple_SetItem(arglist,2,PyUnicode_DecodeUTF8(filename,strlen(filename),NULL));
1315     result = PyEval_CallObject(py_ie[ie_index].export, arglist);
1316     Py_DECREF(arglist);
1317     Py_XDECREF(result);
1318     if ( PyErr_Occurred()!=NULL )
1319 	PyErr_Print();
1320     sc_active_in_ui = NULL;
1321     layer_active_in_ui = ly_fore;
1322 }
1323 
PyFF_registerImportExport(PyObject * UNUSED (self),PyObject * args)1324 static PyObject *PyFF_registerImportExport(PyObject *UNUSED(self), PyObject *args) {
1325     PyObject *import, *export, *data;
1326     char *name, *exten, *exten_list=NULL;
1327     /* I'm assuming the extensions are in ASCII so no conversion will be needed*/
1328 
1329     if ( !PyArg_ParseTuple(args,"OOOss|s", &import, &export, &data,
1330 	    &name, &exten, &exten_list ))
1331 return( NULL );
1332     if ( import==Py_None && export==Py_None )
1333 Py_RETURN_NONE;			/* Well, that was pointless */
1334     if ( import==Py_None )
1335 	import=NULL;
1336     else if ( !PyCallable_Check(import) ) {
1337 	PyErr_Format(PyExc_TypeError, "First argument is not callable" );
1338 return( NULL );
1339     }
1340     if ( export==Py_None )
1341 	export=NULL;
1342     else if ( !PyCallable_Check(export) ) {
1343 	PyErr_Format(PyExc_TypeError, "Second argument is not callable" );
1344 return( NULL );
1345     }
1346 
1347     Py_XINCREF(import);
1348     Py_XINCREF(export);
1349     Py_XINCREF(data);
1350 
1351     if ( ie_cnt>=ie_max )
1352 	py_ie = realloc(py_ie,((ie_max += 10)+1)*sizeof(struct python_import_export));
1353     py_ie[ie_cnt].import = import;
1354     py_ie[ie_cnt].export = export;
1355     py_ie[ie_cnt].data = data;
1356     py_ie[ie_cnt].name = name;
1357     py_ie[ie_cnt].extension = copy(exten);
1358     py_ie[ie_cnt].all_extensions = copy(exten_list==NULL ? exten : exten_list);
1359     ++ie_cnt;
1360     py_ie[ie_cnt].name = NULL;		/* End list marker */
1361 
1362 Py_RETURN_NONE;
1363 }
1364 
PyFF_hasSpiro(PyObject * UNUSED (self),PyObject * UNUSED (args))1365 static PyObject *PyFF_hasSpiro(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1366     PyObject *ret = hasspiro() ? Py_True : Py_False;
1367 
1368     Py_INCREF(ret);
1369 return( ret );
1370 }
1371 
PyFF_SpiroVersion(PyObject * UNUSED (self),PyObject * UNUSED (args))1372 static PyObject *PyFF_SpiroVersion(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1373     char *temp;
1374 
1375     temp=libspiro_version();	/* Return libspiro Version number */
1376 
1377     PyObject *ret=Py_BuildValue("s",temp); free(temp);
1378     return( ret );
1379 }
1380 
1381 GList_Glib* closingFunctionList = 0;
1382 
PyFF_onAppClosing(PyObject * self,PyObject * args)1383 static PyObject *PyFF_onAppClosing(PyObject *self, PyObject *args) {
1384     int cnt;
1385 
1386     cnt = PyTuple_Size(args);
1387 //    printf("PyFF_onAppClosing() cnt:%d\n", cnt );
1388     if ( cnt<1 ) {
1389 	PyErr_Format(PyExc_TypeError, "Too few arguments");
1390 	return( NULL );
1391     }
1392     if (!PyCallable_Check(PyTuple_GetItem(args,0))) {
1393 	PyErr_Format(PyExc_TypeError, "First argument is not callable" );
1394 	return( NULL );
1395     }
1396     PyObject *func = PyTuple_GetItem(args,0);
1397     Py_INCREF(func);
1398     closingFunctionList = g_list_prepend( closingFunctionList, func );
1399 
1400     PyObject *ret = Py_True;
1401     Py_INCREF(ret);
1402     return( ret );
1403 }
1404 
python_call_onClosingFunctions_fe(gpointer data,gpointer udata)1405 static void python_call_onClosingFunctions_fe( gpointer data, gpointer udata )
1406 {
1407     PyObject *func = (PyObject*)data;
1408 //    printf("python_call_onClosingFunctions_fe() f:%p\n", func );
1409     if( func )
1410     {
1411 	PyObject *arglist, *result;
1412 	arglist = PyTuple_New(0);
1413 	result = PyEval_CallObject( func, arglist);
1414 	if ( !result )
1415 	{ // error
1416 	}
1417     }
1418 }
1419 
python_call_onClosingFunctions()1420 void python_call_onClosingFunctions()
1421 {
1422     g_list_foreach( closingFunctionList,
1423 		    python_call_onClosingFunctions_fe, 0 );
1424 }
1425 
1426 /* ************************************************************************** */
1427 /* ************************ User Interface routines ************************* */
1428 /* ************************************************************************** */
1429 
PyFF_registerMenuItemStub(PyObject * UNUSED (self),PyObject * UNUSED (args))1430 static PyObject *PyFF_registerMenuItemStub(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1431 //    printf("PyFF_registerMenuItemStub()\n");
1432     /* This is a stub which will be replaced when we've got a UI */
1433 Py_RETURN_NONE;
1434 }
1435 
PyFF_hasUserInterface(PyObject * UNUSED (self),PyObject * UNUSED (args))1436 static PyObject *PyFF_hasUserInterface(PyObject *UNUSED(self), PyObject *UNUSED(args)) {
1437     PyObject *ret = no_windowing_ui ? Py_False : Py_True;
1438 
1439     Py_INCREF(ret);
1440 return( ret );
1441 }
1442 
PyFF_scriptFromUnicode(PyObject * UNUSED (self),PyObject * args)1443 static PyObject *PyFF_scriptFromUnicode(PyObject *UNUSED(self), PyObject *args) {
1444     unsigned long u;
1445     if ( !PyArg_ParseTuple(args,"k",&u) )
1446 	return( NULL );
1447 
1448     uint32 script = ScriptFromUnicode(u, NULL);
1449     return TagToPythonString(script, false);
1450 }
1451 
PyFF_logError(PyObject * UNUSED (self),PyObject * args)1452 static PyObject *PyFF_logError(PyObject *UNUSED(self), PyObject *args) {
1453     char *msg;
1454     if ( !PyArg_ParseTuple(args,"s", &msg) )
1455 return( NULL );
1456     LogError(msg);
1457 Py_RETURN_NONE;
1458 }
1459 
1460 // This allows the init code to post only less agressive warning
1461 // messages during startup.
1462 static bool showPythonErrors = 1;
1463 
PyFF_postError(PyObject * UNUSED (self),PyObject * args)1464 static PyObject *PyFF_postError(PyObject *UNUSED(self), PyObject *args) {
1465     char *msg, *title;
1466     if ( !PyArg_ParseTuple(args,"ss", &title, &msg) )
1467 return( NULL );
1468     if( showPythonErrors )
1469         ff_post_error(title,msg);		/* Prints to stderr if no ui */
1470 Py_RETURN_NONE;
1471 }
1472 
PyFF_postNotice(PyObject * UNUSED (self),PyObject * args)1473 static PyObject *PyFF_postNotice(PyObject *UNUSED(self), PyObject *args) {
1474     char *msg, *title;
1475     if ( !PyArg_ParseTuple(args,"ss", &title, &msg) )
1476 return( NULL );
1477     ff_post_notice(title,msg);		/* Prints to stderr if no ui */
1478 Py_RETURN_NONE;
1479 }
1480 
PyFF_openFilename(PyObject * UNUSED (self),PyObject * args)1481 static PyObject *PyFF_openFilename(PyObject *UNUSED(self), PyObject *args) {
1482     char *title,*def=NULL,*filter=NULL;
1483     char *ret;
1484     PyObject *reto;
1485 
1486     if ( no_windowing_ui ) {
1487 	PyErr_Format(PyExc_EnvironmentError, "No user interface");
1488 return( NULL );
1489     }
1490 
1491     if ( !PyArg_ParseTuple(args,"s|ss", &title, &def, &filter) )
1492 return( NULL );
1493 
1494     ret = ff_open_filename(title,def,filter);
1495     if ( ret==NULL )
1496 Py_RETURN_NONE;
1497     reto = PyUnicode_DecodeUTF8(ret,strlen(ret),NULL);
1498     free(ret);
1499 return( reto );
1500 }
1501 
PyFF_saveFilename(PyObject * UNUSED (self),PyObject * args)1502 static PyObject *PyFF_saveFilename(PyObject *UNUSED(self), PyObject *args) {
1503     char *title,*def=NULL,*filter=NULL;
1504     char *ret;
1505     PyObject *reto;
1506 
1507     if ( no_windowing_ui ) {
1508 	PyErr_Format(PyExc_EnvironmentError, "No user interface");
1509 return( NULL );
1510     }
1511 
1512     if ( !PyArg_ParseTuple(args,"s|ss", &title, &def, &filter) )
1513 return( NULL );
1514 
1515     ret = ff_save_filename(title,def,filter);
1516     if ( ret==NULL )
1517 Py_RETURN_NONE;
1518     reto = PyUnicode_DecodeUTF8(ret,strlen(ret),NULL);
1519     free(ret);
1520 return( reto );
1521 }
1522 
1523 int NibCheck(SplineSet *ss);
1524 
PyFF_ConvexNibID(const char * tok)1525 int PyFF_ConvexNibID(const char *tok) {
1526    int r = ConvexNibID(tok);
1527    if ( r==-1 )
1528 	PyErr_Format(PyExc_TypeError, "Unrecognized convex nib context name" );
1529    return r;
1530 }
1531 
PyFF_ParseSetConvex(PyObject * args,int * tokp)1532 static SplineSet *PyFF_ParseSetConvex(PyObject *args, int *tokp) {
1533     PyObject *obj;
1534     SplineSet *ss;
1535     char *tok;
1536 
1537     if ( !PyArg_ParseTuple(args,"O|s", &obj, &tok ) ) {
1538 	return NULL;
1539     }
1540     *tokp = PyFF_ConvexNibID(tok);
1541     if ( *tokp==-1 )
1542 	return NULL;
1543 
1544     if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(obj)) ) {
1545 	ss = SSFromLayer( (PyFF_Layer *) obj);
1546     } else if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(obj)) ) {
1547 	ss = SSFromContour( (PyFF_Contour *) obj, NULL);
1548     } else {
1549 	PyErr_Format(PyExc_TypeError, "Argument must be a layer or a contour" );
1550 	return( NULL );
1551     }
1552 
1553     if ( !NibCheck(ss) ) {
1554 	SplinePointListFree(ss);
1555 	return ( NULL );
1556     }
1557 
1558     return ss;
1559 }
1560 
PyFF_setConvexNib(PyObject * UNUSED (self),PyObject * args)1561 static PyObject *PyFF_setConvexNib(PyObject *UNUSED(self), PyObject *args) {
1562     SplineSet *ss;
1563     int toknum;
1564 
1565     ss = PyFF_ParseSetConvex(args, &toknum);
1566     if ( ss==NULL )
1567 	return NULL;
1568 
1569     if ( toknum < 0 && no_windowing_ui ) {
1570 	PyErr_Format(PyExc_TypeError, "No user interface" );
1571 	return NULL;
1572     }
1573     if ( !StrokeSetConvex(ss, toknum) )
1574 	return NULL;
1575 
1576     Py_RETURN_NONE;
1577 }
1578 
PyFF_getConvexNib(PyObject * UNUSED (self),PyObject * args)1579 static PyObject *PyFF_getConvexNib(PyObject *UNUSED(self), PyObject *args) {
1580     const char *tok;
1581     int toknum;
1582     SplineSet *ss;
1583     PyFF_Layer *l;
1584 
1585     if ((tok = PyUnicode_AsUTF8(args)) == NULL) {
1586         return NULL;
1587     }
1588     toknum = PyFF_ConvexNibID(tok);
1589     if ( toknum==-1 )
1590 	return NULL;
1591 
1592     ss = StrokeGetConvex(toknum, false);
1593     if ( ss==NULL )
1594 	Py_RETURN_NONE;
1595     l = LayerFromSS(ss, NULL);
1596     SplinePointListFree(ss);
1597     return (PyObject *) l;
1598 }
1599 
PyFF_ask(PyObject * UNUSED (self),PyObject * args)1600 static PyObject *PyFF_ask(PyObject *UNUSED(self), PyObject *args) {
1601     const char *title=NULL,*quest=NULL, **answers;
1602     int def=0, cancel=-1, cnt;
1603     PyObject *answero;
1604     int i, ret;
1605 
1606     if ( no_windowing_ui ) {
1607 	PyErr_Format(PyExc_EnvironmentError, "No user interface");
1608 return( NULL );
1609     }
1610 
1611     if ( !PyArg_ParseTuple(args,"ssO|ii", &title, &quest, &answero, &def, &cancel) )
1612 return( NULL );
1613     if ( !PySequence_Check(answero) || PyUnicode_Check(answero)) {
1614 	PyErr_Format(PyExc_TypeError, "Expected a tuple of strings for the third argument");
1615 return( NULL );
1616     }
1617     cnt = PySequence_Size(answero);
1618     answers = calloc(cnt+1, sizeof(const char *));
1619     if ( cancel==-1 )
1620 	cancel = cnt-1;
1621     if ( cancel<0 || cancel>=cnt || def<0 || def>=cnt ) {
1622 	PyErr_Format(PyExc_ValueError, "Value out of bounds for 4th or 5th argument");
1623 	free(answers);
1624 return( NULL );
1625     }
1626     for ( i=0; i<cnt; ++i ) {
1627         PyObject *item = PySequence_GetItem(answero, i);
1628         answers[i] = PyUnicode_AsUTF8(item);
1629         Py_XDECREF(item);
1630         if (answers[i] == NULL) {
1631             free(answers);
1632             return NULL;
1633         }
1634     }
1635     answers[cnt] = NULL;
1636 
1637     ret = ff_ask(title,answers,def,cancel,quest);
1638     free(answers);
1639 return( Py_BuildValue("i",ret));
1640 }
1641 
1642 static const char *askChoices_keywords[] = { "title", "question", "answers", "default", "multiple", NULL };
1643 
PyFF_askChoices(PyObject * UNUSED (self),PyObject * args,PyObject * kwargs)1644 static PyObject *PyFF_askChoices(PyObject *UNUSED(self), PyObject *args, PyObject *kwargs) {
1645     const char *title=NULL, *quest=NULL;
1646     const char **answers; // receives answers written into `answero`
1647     PyObject* defo = NULL; // default answer index, or tuple of len cnt
1648     int def = 0; // default index, only used if not multianswer
1649     int cnt; // number of answers
1650     PyObject *multipleo = NULL; // expected to be boolean
1651     bool multiple = false;
1652     PyObject *answero = NULL; // expected to be tuple
1653     int ret;
1654 
1655     if ( no_windowing_ui ) {
1656         PyErr_Format(PyExc_EnvironmentError, "No user interface");
1657         return( NULL );
1658     }
1659 
1660     if ( !PyArg_ParseTupleAndKeywords(args, kwargs, "ssO|OO", (char**) askChoices_keywords,
1661                                       &title, &quest, &answero, &defo, &multipleo) ) {
1662         PyErr_Format(PyExc_EnvironmentError, "Failed to parse arguments");
1663         return( NULL );
1664     }
1665 
1666     multiple = multipleo != NULL && PyObject_IsTrue(multipleo);
1667 
1668     if ( !PySequence_Check(answero) || PyUnicode_Check(answero)) {
1669         PyErr_Format(PyExc_TypeError, "Expected a tuple of strings for the third argument");
1670         return( NULL );
1671     }
1672 
1673     cnt = PySequence_Size(answero);
1674     char* sel = calloc(cnt, sizeof(char));
1675 
1676     if (defo != NULL && defo != Py_None) {
1677         if ( PyLong_Check(defo) ) {
1678             def = (int)PyLong_AsLong(defo);
1679             if ( def<0 || def>=cnt ) {
1680                 PyErr_Format(PyExc_ValueError, "Value out of bounds for 4th argument");
1681                 free(sel);
1682                 return( NULL );
1683             }
1684             if (multiple) {
1685                 sel[def] = (char)1;
1686             }
1687         } else if ( PyTuple_Check(defo) ) {
1688             int dcnt = PySequence_Size(defo);
1689             if (dcnt != cnt) {
1690                 PyErr_Format(PyExc_ValueError, "Expected tuple/list of %d items, got %d items", cnt, dcnt );
1691                 free(sel);
1692                 return( NULL );
1693             }
1694             bool def_set = false;
1695             for (int i = 0; i < cnt; i++) {
1696                 PyObject* temp = PyTuple_GetItem(defo, i);
1697                 sel[i] = PyObject_IsTrue(temp);
1698                 if (!multiple && sel[i]) {
1699                     if (def_set) {
1700                         PyErr_Format(PyExc_ValueError, "`multiple` False, only expected 1 True in `default`" );
1701                         free(sel);
1702                         return( NULL );
1703                     }
1704                     def = i;
1705                     def_set = true;
1706                 }
1707             }
1708         } else {
1709             PyErr_Format(PyExc_TypeError, "4th argument must be a tuple or integer" );
1710             free(sel);
1711             return( NULL );
1712         }
1713     }
1714 
1715     answers = calloc(cnt+1, sizeof(const char *));
1716     for ( int i=0; i<cnt; ++i ) {
1717         PyObject *temp = PySequence_GetItem(answero,i);
1718         answers[i] = PyUnicode_AsUTF8(temp);
1719         Py_XDECREF(temp);
1720         if (answers[i] == NULL) {
1721             free(answers);
1722             free(sel);
1723             return NULL;
1724         }
1725     }
1726     answers[cnt] = NULL;
1727 
1728     PyObject* reto;
1729     if (multiple) {
1730         char* buts[2] = {_("OK"), _("Cancel")};
1731         reto = PyTuple_New(cnt);
1732         ret = ff_choose_multiple(title, answers, sel, cnt, buts, quest);
1733         for (int i = 0; i < cnt; i++) {
1734             PyObject* o = (sel[i] == 1) ? Py_True : Py_False;
1735             PyTuple_SetItem(reto, i, o);
1736             Py_INCREF(o); // prevents crash on finalize
1737         }
1738     } else {
1739         ret = ff_choose(title, answers, cnt, def, quest);
1740     }
1741 
1742     free(sel);
1743     free(answers);
1744 
1745     if (multiple) {
1746         return ( reto );
1747     }
1748     return( Py_BuildValue("i", ret) );
1749 }
1750 
PyFF_askString(PyObject * UNUSED (self),PyObject * args)1751 static PyObject *PyFF_askString(PyObject *UNUSED(self), PyObject *args) {
1752     char *title,*quest, *def = NULL;
1753     char *ret;
1754     PyObject *reto;
1755 
1756     if ( no_windowing_ui ) {
1757 	PyErr_Format(PyExc_EnvironmentError, "No user interface");
1758 return( NULL );
1759     }
1760 
1761     if ( !PyArg_ParseTuple(args,"ss|s", &title, &quest, &def) )
1762 return( NULL );
1763 
1764     ret = ff_ask_string(title,def,quest);
1765     if ( ret==NULL )
1766 Py_RETURN_NONE;
1767     reto = Py_BuildValue("s",ret);
1768     free(ret);
1769 return( reto );
1770 }
1771 
1772 /* ************************************************************************** */
1773 /* Points */
1774 /* ************************************************************************** */
1775 
1776 static const char *py_point_types[] = { "splineCorner", "splineCurve", "splineHVCurve",
1777 				     "splineTangent", NULL };
1778 #define MAX_POINTTYPE_VAL 3
1779 
PyFF_ConvertToPointType(int val)1780 static enum pointtype PyFF_ConvertToPointType(int val) {
1781     switch(val) {
1782 	case 0: return pt_corner;
1783 	case 1: return pt_curve;
1784 	case 2: return pt_hvcurve;
1785 	case 3: return pt_tangent;
1786     }
1787     return pt_corner;
1788 }
1789 
PyFF_ConvertFromPointType(enum pointtype pt)1790 static int PyFF_ConvertFromPointType(enum pointtype pt) {
1791     switch(pt) {
1792 	case pt_curve: return 1;
1793 	case pt_corner: return 0;
1794 	case pt_tangent: return 3;
1795 	case pt_hvcurve: return 2;
1796     }
1797     return 0;
1798 }
1799 
AddPointConstants(PyObject * module)1800 static void AddPointConstants( PyObject *module ) {
1801     int i;
1802     for ( i=0; py_point_types[i]!=NULL; ++i )
1803         PyModule_AddObject(module, py_point_types[i], Py_BuildValue("i",i));
1804 }
1805 
PyFFPoint_CNew(double x,double y,int on_curve,int sel,int type,char * name)1806 static PyFF_Point *PyFFPoint_CNew(double x, double y, int on_curve, int sel, int type, char *name) {
1807     /* Convenience routine for creating a new point from C */
1808     PyFF_Point *self = (PyFF_Point *)PyFF_PointType.tp_alloc(&PyFF_PointType, 0);
1809     if ( self==NULL )
1810 	return( NULL );
1811     self->x = x;
1812     self->y = y;
1813     self->on_curve = on_curve;
1814     self->selected = sel;
1815     self->type = type;
1816     self->name = copy(name);
1817     return( self );
1818 }
1819 
PyFFPoint_dup(PyFF_Point * self)1820 static PyObject *PyFFPoint_dup(PyFF_Point *self) {
1821     PyFF_Point *ret = PyFFPoint_CNew(self->x, self->y, self->on_curve,
1822                                      self->selected, self->type, self->name);
1823     ret->interpolated = self->interpolated;
1824     return( (PyObject *) ret );
1825 }
1826 
PyFFPoint_Parse(PyObject * args,bool dup,bool always)1827 static PyFF_Point *PyFFPoint_Parse(PyObject *args, bool dup, bool always) {
1828     double x,y;
1829     PyFF_Point *p=NULL;
1830     int i, on, sel, type, interp;
1831 
1832     if ( args==NULL && !always )
1833 	return NULL;
1834 
1835     x = y = 0.0;
1836     on = true;
1837     sel = false;
1838     interp = false;
1839     type = PyFF_ConvertFromPointType(pt_corner);
1840     if ( !PyArg_ParseTuple( args, "(ddi)|ii", &x, &y, &on, &type, &sel )) {
1841 	PyErr_Clear();
1842 	if ( !PyArg_ParseTuple( args, "(dd)|iii", &x, &y, &on, &type, &sel )) {
1843 	    PyErr_Clear();
1844 	    if ( !PyArg_ParseTuple( args, "dd|iiii", &x, &y, &on, &type, &sel, &interp )) {
1845 		PyErr_Clear();
1846 		if ( PyType_IsSubtype(&PyFF_PointType, Py_TYPE(args)) ) {
1847 		    p = (PyFF_Point *) args;
1848 		} else if ( !always ) {
1849 		    return( NULL );
1850 		}
1851 	    }
1852 	}
1853     }
1854 
1855     if ( type < 0 || type > MAX_POINTTYPE_VAL ) {
1856 	PyErr_Format(PyExc_TypeError, "Unrecognized point type");
1857 	return NULL;
1858     }
1859     if ( p==NULL ) {
1860 	p = PyFFPoint_CNew(x,y,on,sel,type,NULL);
1861 	if ( p==NULL )
1862 	    return( NULL );
1863 	if ( interp )
1864 	    p->interpolated = true;
1865     } else if ( dup ) {
1866 	p = (PyFF_Point *) PyFFPoint_dup(p);
1867     } else {
1868 	Py_INCREF( p );
1869     }
1870     return p;
1871 }
1872 
PyFF_TransformPoint(PyFF_Point * self,double transform[6])1873 static void PyFF_TransformPoint(PyFF_Point *self, double transform[6]) {
1874     double x,y;
1875 
1876     x = transform[0]*(double)self->x + transform[2]*(double)self->y + transform[4];
1877     y = transform[1]*(double)self->x + transform[3]*(double)self->y + transform[5];
1878     self->x = rint(1024*x)/1024;
1879     self->y = rint(1024*y)/1024;
1880 }
1881 
PyFFPoint_Transform(PyFF_Point * self,PyObject * args)1882 static PyObject *PyFFPoint_Transform(PyFF_Point *self, PyObject *args) {
1883     double m[6];
1884 
1885     if ( !PyArg_ParseTuple(args,"(dddddd)",&m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) )
1886 return( NULL );
1887     PyFF_TransformPoint(self,m);
1888     Py_INCREF((PyObject *) self);
1889 Py_RETURN( self );
1890 }
1891 
PyFFPoint_pickleReducer(PyFF_Point * self,PyObject * UNUSED (args))1892 static PyObject *PyFFPoint_pickleReducer(PyFF_Point *self, PyObject *UNUSED(args)) {
1893     PyObject *reductionTuple, *argTuple;
1894 
1895     if ( _new_point==NULL )
1896 	PyFF_PickleTypesInit();
1897     reductionTuple = PyTuple_New(2);
1898     Py_INCREF(_new_point);
1899     PyTuple_SetItem(reductionTuple,0,_new_point);
1900     argTuple = PyTuple_New(6);
1901     PyTuple_SetItem(reductionTuple,1,argTuple);
1902     PyTuple_SetItem(argTuple,0,Py_BuildValue("d", (double)self->x));
1903     PyTuple_SetItem(argTuple,1,Py_BuildValue("d", (double)self->y));
1904     PyTuple_SetItem(argTuple,2,Py_BuildValue("i", self->on_curve));
1905     PyTuple_SetItem(argTuple,3,Py_BuildValue("i", self->type));
1906     PyTuple_SetItem(argTuple,4,Py_BuildValue("i", self->selected));
1907     PyTuple_SetItem(argTuple,5,Py_BuildValue("i", self->interpolated));
1908 return( reductionTuple );
1909 }
1910 
PyFFPoint_compare(PyFF_Point * self,PyObject * other)1911 static int PyFFPoint_compare(PyFF_Point *self,PyObject *other) {
1912     double x, y;
1913 
1914     if ( !PyArg_ParseTuple(other,"dd", &x, &y ) ) {
1915 	PyErr_Clear();
1916 	if ( PyType_IsSubtype(&PyFF_PointType, Py_TYPE(other)) ) {
1917 	    x = ((PyFF_Point *) other)->x;
1918 	    y = ((PyFF_Point *) other)->y;
1919 	} else {
1920 	    PyErr_Format(PyExc_TypeError, "Unexpected type");
1921 	    return( -1 );
1922 	}
1923     }
1924 
1925     if ( RealNear(self->x,x) ) {
1926 	if ( RealNear(self->y,y))
1927 return( 0 );
1928 	else if ( (double)self->y>y )
1929 return( 1 );
1930     } else if ( (double)self->x>x )
1931 return( 1 );
1932 
1933 return( -1 );
1934 }
1935 
PyFFPoint_get_name(PyFF_Point * self,void * UNUSED (closure))1936 static PyObject *PyFFPoint_get_name(PyFF_Point *self, void *UNUSED(closure)) {
1937     if (self->name==NULL)
1938 	Py_RETURN_NONE;
1939     return (Py_BuildValue("s", self->name));
1940 }
1941 
PyFFPoint_set_name(PyFF_Point * self,PyObject * value,void * UNUSED (closure))1942 static int PyFFPoint_set_name(PyFF_Point *self,PyObject *value, void *UNUSED(closure)) {
1943     if (value == Py_None) {
1944         free(self->name);
1945         self->name = NULL;
1946     } else {
1947         char *name = copy(PyUnicode_AsUTF8(value));
1948         if (name == NULL) {
1949             return -1;
1950         }
1951         free(self->name);
1952         self->name = name;
1953     }
1954     return 0;
1955 }
1956 
PyFFPoint_richcompare(PyObject * a,PyObject * b,int op)1957 static PyObject *PyFFPoint_richcompare(PyObject *a, PyObject *b, int op) {
1958     return enrichened_compare((cmpfunc) PyFFPoint_compare, a, b, op);
1959 }
1960 
PyFFPoint_Repr(PyFF_Point * self)1961 static PyObject *PyFFPoint_Repr(PyFF_Point *self) {
1962     char buffer[200];
1963 
1964     sprintf(buffer,"%s(%g,%g,%s)", Py_TYPE(self)->tp_name, (double)self->x, (double)self->y,
1965 	    self->on_curve?"True":"False" );
1966 return( PyUnicode_FromString(buffer));
1967 }
1968 
PyFFPoint_Str(PyFF_Point * self)1969 static PyObject *PyFFPoint_Str(PyFF_Point *self) {
1970     char buffer[200];
1971 
1972     sprintf(buffer,"<FFPoint (%g,%g) %s>", (double)self->x, (double)self->y, self->on_curve?"on":"off" );
1973 return( PyUnicode_FromString(buffer));
1974 }
1975 
1976 static PyMemberDef FFPoint_members[] = {
1977     {(char *)"x", T_DOUBLE, offsetof(PyFF_Point, x), 0,
1978      (char *)"x coordinate"},
1979     {(char *)"y", T_DOUBLE, offsetof(PyFF_Point, y), 0,
1980      (char *)"y coordinate"},
1981     {(char *)"on_curve", T_UBYTE, offsetof(PyFF_Point, on_curve), 0,
1982      (char *)"whether this point lies on the curve or is a control point"},
1983     {(char *)"selected", T_UBYTE, offsetof(PyFF_Point, selected), 0,
1984      (char *)"whether this point is selected"},
1985     {(char *)"type", T_UBYTE, offsetof(PyFF_Point, type), 0,
1986      (char *)"the FontForge spline point type"},
1987     {(char *)"interpolated", T_UBYTE, offsetof(PyFF_Point, interpolated), 0,
1988      (char *)"whether this (quadratic) point is interpolated"},
1989     {NULL, 0, 0, 0, NULL}  /* Sentinel */
1990 };
1991 
1992 static PyMethodDef FFPoint_methods[] = {
1993     {(char *)"dup", (PyCFunction)PyFFPoint_dup, METH_NOARGS,
1994      (char *)"Returns a copy of this point" },
1995     {(char *)"transform", (PyCFunction)PyFFPoint_Transform, METH_VARARGS,
1996      (char *)"Transforms the point by the transformation matrix (a 6 element tuple of reals)" },
1997     {(char *)"__reduce__", (PyCFunction)PyFFPoint_pickleReducer, METH_NOARGS,
1998      (char *)"cPickle calls this routine when it wants to pickle us" },
1999     PYMETHODDEF_EMPTY /* Sentinel */
2000 };
2001 
2002 static PyGetSetDef FFPoint_getset[] = {
2003         {(char *)"name",
2004      (getter)PyFFPoint_get_name, (setter)PyFFPoint_set_name,
2005      (char *)"Points may be named", NULL},
2006     PYGETSETDEF_EMPTY /* Sentinel */
2007 };
2008 
PyFFPoint_New(PyTypeObject * UNUSED (type),PyObject * args,PyObject * UNUSED (kwds))2009 static PyObject *PyFFPoint_New(PyTypeObject *UNUSED(type), PyObject *args, PyObject *UNUSED(kwds)) {
2010     PyFF_Point *self = PyFFPoint_Parse(args, true, true);
2011     return( (PyObject *)self );
2012 }
2013 
2014 static PyTypeObject PyFF_PointType = {
2015     PyVarObject_HEAD_INIT(NULL, 0)
2016     "fontforge.point",         /* tp_name */
2017     sizeof(PyFF_Point),        /* tp_basicsize */
2018     0,                         /* tp_itemsize */
2019     NULL,                      /* tp_dealloc */
2020     0,                         /* tp_vectorcall_offset */
2021     NULL,                      /* tp_getattr */
2022     NULL,                      /* tp_setattr */
2023     NULL,                      /* tp_reserved / tp_compare */
2024     (reprfunc) PyFFPoint_Repr, /* tp_repr */
2025     NULL,                      /* tp_as_number */
2026     NULL,                      /* tp_as_sequence */
2027     NULL,                      /* tp_as_mapping */
2028     NULL,                      /* tp_hash */
2029     NULL,                      /* tp_call */
2030     (reprfunc) PyFFPoint_Str,  /* tp_str */
2031     NULL,                      /* tp_getattro */
2032     NULL,                      /* tp_setattro */
2033     NULL,                      /* tp_as_buffer */
2034     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
2035     "fontforge Point objects", /* tp_doc */
2036     NULL,                      /* tp_traverse */
2037     NULL,                      /* tp_clear */
2038     (richcmpfunc) PyFFPoint_richcompare, /* tp_richcompare */
2039     0,                         /* tp_weaklistoffset */
2040     NULL,                      /* tp_iter */
2041     NULL,                      /* tp_iternext */
2042     FFPoint_methods,           /* tp_methods */
2043     FFPoint_members,           /* tp_members */
2044     FFPoint_getset,            /* tp_getset */
2045     NULL,                      /* tp_base */
2046     NULL,                      /* tp_dict */
2047     NULL,                      /* tp_descr_get */
2048     NULL,                      /* tp_descr_set */
2049     0,                         /* tp_dictoffset */
2050     NULL,                      /* tp_init */
2051     NULL,                      /* tp_alloc */
2052     PyFFPoint_New,             /* tp_new */
2053     NULL,                      /* tp_free */
2054     NULL,                      /* tp_is_gc */
2055     NULL,                      /* tp_bases */
2056     NULL,                      /* tp_mro */
2057     NULL,                      /* tp_cache */
2058     NULL,                      /* tp_subclasses */
2059     NULL,                      /* tp_weaklist */
2060     NULL,                      /* tp_del */
2061     0,                         /* tp_version_tag */
2062 };
2063 
2064 /* ************************************************************************** */
2065 /* Contour iterator type */
2066 /* ************************************************************************** */
2067 
2068 typedef struct {
2069     PyObject_HEAD
2070     int pos;
2071     PyFF_Contour *contour;
2072 } contouriterobject;
2073 static PyTypeObject PyFF_ContourIterType;
2074 
contouriter_new(PyObject * contour)2075 static PyObject *contouriter_new(PyObject *contour) {
2076     contouriterobject *ci;
2077     ci = PyObject_New(contouriterobject, &PyFF_ContourIterType);
2078     if (ci == NULL)
2079 return NULL;
2080     ci->contour = ((PyFF_Contour *) contour);
2081     Py_INCREF(contour);
2082     ci->pos = 0;
2083 return (PyObject *)ci;
2084 }
2085 
contouriter_dealloc(contouriterobject * ci)2086 static void contouriter_dealloc(contouriterobject *ci) {
2087     Py_XDECREF(ci->contour);
2088     PyObject_Del(ci);
2089 }
2090 
contouriter_iternext(contouriterobject * ci)2091 static PyObject *contouriter_iternext(contouriterobject *ci) {
2092     PyFF_Contour *contour = ci->contour;
2093     PyObject *pt;
2094 
2095     if ( contour == NULL)
2096 return NULL;
2097 
2098     if ( ci->pos<contour->pt_cnt ) {
2099 	pt = (PyObject *) contour->points[ci->pos++];
2100 	Py_INCREF(pt);
2101 return( pt );
2102     }
2103 
2104 return NULL;
2105 }
2106 
2107 static PyTypeObject PyFF_ContourIterType = {
2108     PyVarObject_HEAD_INIT(NULL, 0)
2109     "fontforge.contour_iterator", /* tp_name */
2110     sizeof(contouriterobject), /* tp_basicsize */
2111     0,                         /* tp_itemsize */
2112     /* methods */
2113     (destructor)contouriter_dealloc, /* tp_dealloc */
2114     0,                         /* tp_vectorcall_offset */
2115     NULL,                      /* tp_getattr */
2116     NULL,                      /* tp_setattr */
2117     NULL,                      /* tp_compare */
2118     NULL,                      /* tp_repr */
2119     NULL,                      /* tp_as_number */
2120     NULL,                      /* tp_as_sequence */
2121     NULL,                      /* tp_as_mapping */
2122     NULL,                      /* tp_hash */
2123     NULL,                      /* tp_call */
2124     NULL,                      /* tp_str */
2125     NULL,                      /* tp_getattro */
2126     NULL,                      /* tp_setattro */
2127     NULL,                      /* tp_as_buffer */
2128     Py_TPFLAGS_DEFAULT,        /* tp_flags */
2129     NULL,                      /* tp_doc */
2130     NULL,                      /* tp_traverse */
2131     NULL,                      /* tp_clear */
2132     NULL,                      /* tp_richcompare */
2133     0,                         /* tp_weaklistoffset */
2134     PyObject_SelfIter,         /* tp_iter */
2135     (iternextfunc)contouriter_iternext,	/* tp_iternext */
2136     NULL,                      /* tp_methods */
2137     NULL,                      /* tp_members */
2138     NULL,                      /* tp_getset */
2139     NULL,                      /* tp_base */
2140     NULL,                      /* tp_dict */
2141     NULL,                      /* tp_descr_get */
2142     NULL,                      /* tp_descr_set */
2143     0,                         /* tp_dictoffset */
2144     NULL,                      /* tp_init */
2145     NULL,                      /* tp_alloc */
2146     NULL,                      /* tp_new */
2147     NULL,                      /* tp_free */
2148     NULL,                      /* tp_is_gc */
2149     NULL,                      /* tp_bases */
2150     NULL,                      /* tp_mro */
2151     NULL,                      /* tp_cache */
2152     NULL,                      /* tp_subclasses */
2153     NULL,                      /* tp_weaklist */
2154     NULL,                      /* tp_del */
2155     0,                         /* tp_version_tag */
2156 };
2157 
2158 /* ************************************************************************** */
2159 /* Contours */
2160 /* ************************************************************************** */
2161 
PyFFContour_clear(PyFF_Contour * self)2162 static int PyFFContour_clear(PyFF_Contour *self) {
2163     int i;
2164 
2165     for ( i=0; i<self->pt_cnt; ++i )
2166 	Py_DECREF(self->points[i]);
2167     self->pt_cnt = 0;
2168 
2169 return 0;
2170 }
2171 
PyFFContour_dealloc(PyFF_Contour * self)2172 static void PyFFContour_dealloc(PyFF_Contour *self) {
2173     PyFFContour_clear(self);
2174     PyMem_Del(self->points);
2175     if ( self->spiro_cnt!=0 )
2176 	PyMem_Del(self->spiros);
2177     free(self->name);
2178     Py_TYPE(self)->tp_free((PyObject*)self);
2179 }
2180 
PyFFContour_ClearSpiros(PyFF_Contour * self)2181 static void PyFFContour_ClearSpiros(PyFF_Contour *self) {
2182     if ( self->spiro_cnt!=0 )
2183 	free(self->spiros);
2184     self->spiros = NULL;
2185     self->spiro_cnt = 0;
2186 }
2187 
PyFFContour_new(PyTypeObject * type,PyObject * UNUSED (args),PyObject * UNUSED (kwds))2188 static PyObject *PyFFContour_new(PyTypeObject *type, PyObject *UNUSED(args), PyObject *UNUSED(kwds)) {
2189     PyFF_Contour *self;
2190 
2191     self = (PyFF_Contour *)type->tp_alloc(type, 0);
2192     if ( self!=NULL ) {
2193 	self->points = NULL;
2194 	self->pt_cnt = self->pt_max = 0;
2195 	self->is_quadratic = false;
2196 	self->closed = 0;
2197 	self->spiro_cnt = 0;
2198 	self->name = NULL;
2199     }
2200     return (PyObject *)self;
2201 }
2202 
PyFFContour_init(PyFF_Contour * self,PyObject * args,PyObject * UNUSED (kwds))2203 static int PyFFContour_init(PyFF_Contour *self, PyObject *args, PyObject *UNUSED(kwds)) {
2204     int quad=0;
2205 
2206     if ( args!=NULL && !PyArg_ParseTuple(args, "|i", &quad))
2207 return -1;
2208 
2209     self->is_quadratic = (quad!=0);
2210 return 0;
2211 }
2212 
PyFFContour_Str(PyFF_Contour * self)2213 static PyObject *PyFFContour_Str(PyFF_Contour *self) {
2214     char *buffer, *pt;
2215     int i;
2216     PyObject *ret;
2217 
2218     buffer=pt=malloc(self->pt_cnt*30+30);
2219     strcpy(buffer, self->is_quadratic? "<Contour(quadratic)\n":"<Contour(cubic)\n");
2220     pt = buffer+strlen(buffer);
2221     for ( i=0; i<self->pt_cnt; ++i ) {
2222 	sprintf( pt, "  (%g,%g) %s\n", (double)self->points[i]->x, (double)self->points[i]->y,
2223 		self->points[i]->on_curve ? "on" : "off" );
2224 	pt += strlen( pt );
2225     }
2226     strcpy(pt,">");
2227     ret = PyUnicode_FromString( buffer );
2228     free( buffer );
2229 return( ret );
2230 }
2231 
PyFFContour_docompare(PyFF_Contour * self,PyObject * other,double pt_err,double spline_err)2232 static int PyFFContour_docompare(PyFF_Contour *self,PyObject *other,
2233 	double pt_err, double spline_err) {
2234     SplineSet *ss, *ss2;
2235     int ret;
2236     SplinePoint *badpoint;
2237 
2238     if ( !PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(other)) ) {
2239 	PyErr_Format(PyExc_TypeError, "Unexpected type");
2240 return( -1 );
2241     }
2242     ss = SSFromContour(self,NULL);
2243     ss2 = SSFromContour((PyFF_Contour *) other,NULL);
2244     ret = SSsCompare(ss,ss2,pt_err,spline_err,&badpoint);
2245     SplinePointListFree(ss);
2246     SplinePointListFree(ss2);
2247 return(ret);
2248 }
2249 
PyFFContour_compare(PyFF_Contour * self,PyObject * other)2250 static int PyFFContour_compare(PyFF_Contour *self,PyObject *other) {
2251     const double pt_err = .5, spline_err = 1;
2252     int i,ret;
2253 
2254     ret = PyFFContour_docompare(self,other,pt_err,spline_err);
2255     if ( !(ret&SS_NoMatch) )
2256 return( 0 );
2257     /* There's no real ordering on these guys. Make up something that is */
2258     /*  at least consistent */
2259     if ( self->pt_cnt < ((PyFF_Contour *) other)->pt_cnt )
2260 return( -1 );
2261     else if ( self->pt_cnt > ((PyFF_Contour *) other)->pt_cnt )
2262 return( 1 );
2263     /* If there's a difference, then there must be at least one point to be */
2264     /*  different. And we only get here if there were a difference */
2265     for ( i=0; i<self->pt_cnt; ++i ) {
2266 	ret = PyFFPoint_compare(self->points[i],(PyObject *) (((PyFF_Contour *) other)->points[i]) );
2267 	if ( ret!=0 )
2268 return( ret );
2269     }
2270 return( -1 );		/* Arbitrary... but we can't get here=>all points same */
2271 }
2272 
PyFFContour_richcompare(PyObject * a,PyObject * b,int op)2273 static PyObject *PyFFContour_richcompare(PyObject *a, PyObject *b, int op) {
2274     return enrichened_compare((cmpfunc) PyFFContour_compare, a, b, op);
2275 }
2276 
2277 /* ************************************************************************** */
2278 /* Contour getters/setters */
2279 /* ************************************************************************** */
PyFF_Contour_get_is_quadratic(PyFF_Contour * self,void * UNUSED (closure))2280 static PyObject *PyFF_Contour_get_is_quadratic(PyFF_Contour *self, void *UNUSED(closure)) {
2281 return( Py_BuildValue("i", self->is_quadratic ));
2282 }
2283 
PyFF_Contour_set_is_quadratic(PyFF_Contour * self,PyObject * value,void * UNUSED (closure))2284 static int PyFF_Contour_set_is_quadratic(PyFF_Contour *self,PyObject *value, void *UNUSED(closure)) {
2285     int val;
2286     SplineSet *ss, *ss2;
2287 
2288     val = PyLong_AsLong(value);
2289     if ( PyErr_Occurred()!=NULL )
2290 return( -1 );
2291 
2292     val = (val!=0);
2293     if ( val == self->is_quadratic )
2294 return( 0 );
2295     if ( self->pt_cnt!=0 ) {
2296 	ss = SSFromContour(self,NULL);
2297 	PyFFContour_clear(self);
2298         if ( ss==NULL ) {
2299 	    if ( PyErr_Occurred() != NULL ) {
2300                 return( -1 );
2301 	    } else {
2302 		// Empty contour
2303 		self->is_quadratic = (val!=0);
2304 		return ( 0 );
2305 	    }
2306         }
2307 	if ( val )
2308 	    ss2 = SplineSetsTTFApprox(ss);
2309 	else
2310 	    ss2 = SplineSetsPSApprox(ss);
2311 	SplinePointListFree(ss);
2312 	ContourFromSS(ss2,self);
2313 	SplinePointListFree(ss2);
2314     }
2315     self->is_quadratic = (val!=0);
2316     return( 0 );
2317 }
2318 
PyFF_Contour_get_closed(PyFF_Contour * self,void * UNUSED (closure))2319 static PyObject *PyFF_Contour_get_closed(PyFF_Contour *self, void *UNUSED(closure)) {
2320 return( Py_BuildValue("i", self->closed ));
2321 }
2322 
PyFF_Contour_set_closed(PyFF_Contour * self,PyObject * value,void * UNUSED (closure))2323 static int PyFF_Contour_set_closed(PyFF_Contour *self,PyObject *value, void *UNUSED(closure)) {
2324     int val;
2325 
2326     val = PyLong_AsLong(value);
2327     if ( PyErr_Occurred()!=NULL )
2328 return( -1 );
2329 
2330     val = (val!=0);
2331     if ( val == self->closed )
2332 return( 0 );
2333     PyFFContour_ClearSpiros((PyFF_Contour *) self);
2334     if ( !val ) {
2335 	self->closed = false;
2336 	if ( self->pt_cnt>1 && self->points[0]->on_curve )
2337 	    self->points[self->pt_cnt++] = PyFFPoint_CNew(self->points[0]->x,self->points[0]->y,true,false,0,NULL);
2338     } else {
2339 	self->closed = true;
2340 	if ( self->pt_cnt>1 && self->points[0]->on_curve &&
2341 		self->points[self->pt_cnt-1]->on_curve &&
2342 		self->points[0]->x == self->points[self->pt_cnt-1]->x &&
2343 		self->points[0]->y == self->points[self->pt_cnt-1]->y ) {
2344 	    --self->pt_cnt;
2345 	    Py_DECREF(self->points[self->pt_cnt]);
2346 	}
2347     }
2348 return( 0 );
2349 }
2350 
PyFF_Contour_get_spiros(PyFF_Contour * self,void * UNUSED (closure))2351 static PyObject *PyFF_Contour_get_spiros(PyFF_Contour *self, void *UNUSED(closure)) {
2352     PyObject *spirotuple;
2353     int i;
2354 
2355     if ( !hasspiro()) {
2356 	PyErr_Format(PyExc_EnvironmentError, "Spiros not available. Please install libspiro before continuing" );
2357 return( NULL );
2358     }
2359     if ( self->spiro_cnt==0 ) {
2360 	SplineSet *ss;
2361 	uint16 cnt;
2362 	ss = SSFromContour(self,NULL);
2363         if ( ss==NULL ) {
2364 	    if ( PyErr_Occurred() == NULL )
2365 		// XXX Should this really be an error?
2366 		PyErr_SetString(PyExc_AttributeError, "Empty Contour");
2367             return( NULL );
2368         }
2369 	self->spiros = SplineSet2SpiroCP(ss,&cnt);
2370 	self->spiro_cnt = cnt;
2371     }
2372     spirotuple = PyTuple_New(self->spiro_cnt-1);
2373     for ( i=0; i<self->spiro_cnt-1; ++i ) {
2374 	int ty = self->spiros[i].ty & 0x7f;
2375 	PyTuple_SetItem(spirotuple,i,Py_BuildValue("ddii",
2376 		self->spiros[i].x, self->spiros[i].y,
2377 		ty==SPIRO_G4 ? 1 :
2378 		ty==SPIRO_G2 ? 2 :
2379 		ty==SPIRO_CORNER ? 3 :
2380 		ty==SPIRO_LEFT ? 4 :
2381 		ty==SPIRO_RIGHT ? 5 : 6,
2382 		(self->spiros[i].ty&0x80)>>7 ));
2383     }
2384 return( spirotuple );
2385 }
2386 
PyFF_Contour_set_spiros(PyFF_Contour * self,PyObject * value,void * UNUSED (closure))2387 static int PyFF_Contour_set_spiros(PyFF_Contour *self,PyObject *value, void *UNUSED(closure)) {
2388     PyObject *spirotuple = value;
2389     int i, cnt;
2390     spiro_cp *spiros;
2391     SplineSet *ss;
2392 
2393     if ( !hasspiro()) {
2394 	PyErr_Format(PyExc_EnvironmentError, "Spiros not available. Please install libspiro before continuing" );
2395 return( -1 );
2396     }
2397     if ( !PySequence_Check(spirotuple)) {
2398 	PyErr_Format(PyExc_TypeError, "Please specify a tuple of spiro control points" );
2399 return( -1 );
2400     }
2401     cnt = PySequence_Size(spirotuple);
2402     if ( cnt < 1 ) {
2403 	PyErr_Format(PyExc_TypeError, "Please specify a tuple of spiro control points" );
2404         return( -1 );
2405     }
2406     PyFFContour_ClearSpiros((PyFF_Contour *) self);
2407     spiros = malloc((cnt+1)*sizeof(spiro_cp));
2408     spiros[cnt].x = spiros[cnt].y = 0;
2409     spiros[cnt].ty = SPIRO_END;
2410     for ( i=0; i<cnt; ++i ) {
2411 	double x,y; int type,flags=0;
2412 	if ( !PyArg_ParseTuple(PySequence_GetItem(spirotuple,i),"ddi|i",&x,&y,&type,&flags)) {
2413 	    PyErr_Format(PyExc_TypeError, "Please specify a tuple of spiro control points" );
2414 	    free(spiros);
2415 return( -1 );
2416 	}
2417 	spiros[i].x = x;
2418 	spiros[i].y = y;
2419 	if ( type==1 )
2420 	    spiros[i].ty = SPIRO_G4;
2421 	else if ( type==2 )
2422 	    spiros[i].ty = SPIRO_G2;
2423 	else if ( type==3 )
2424 	    spiros[i].ty = SPIRO_CORNER;
2425 	else if ( type==4 )
2426 	    spiros[i].ty = SPIRO_LEFT;
2427 	else if ( type==5 )
2428 	    spiros[i].ty = SPIRO_RIGHT;
2429 	else if ( type==6 && i==0 )
2430 	    spiros[i].ty = SPIRO_OPEN_CONTOUR;
2431 	else {
2432 	    PyErr_Format(PyExc_TypeError, "Unknown spiro control point type: %d", type );
2433 	    free(spiros);
2434 return( -1 );
2435 	}
2436 	if ( flags==1 )
2437 	    SPIRO_SELECT(&spiros[i]);
2438 	else if ( flags!=0 ) {
2439 	    PyErr_Format(PyExc_TypeError, "Unexpected value for flags: %d", flags );
2440 	    free(spiros);
2441 return( -1 );
2442 	}
2443     }
2444     ss = SpiroCP2SplineSet(spiros);
2445     ss->spiros = NULL; ss->spiro_cnt = ss->spiro_max = 0;
2446     ContourFromSS(ss,self);
2447     self->spiro_cnt = cnt+1;
2448     self->spiros = spiros;
2449     SplinePointListFree(ss);
2450 return( 0 );
2451 }
2452 
PyFF_Contour_get_name(PyFF_Contour * self,void * UNUSED (closure))2453 static PyObject *PyFF_Contour_get_name(PyFF_Contour *self, void *UNUSED(closure)) {
2454     if ( self->name==NULL )
2455 Py_RETURN_NONE;
2456 
2457 return( Py_BuildValue("s", self->name ));
2458 }
2459 
PyFF_Contour_set_name(PyFF_Contour * self,PyObject * value,void * UNUSED (closure))2460 static int PyFF_Contour_set_name(PyFF_Contour *self,PyObject *value, void *UNUSED(closure)) {
2461     if (value == Py_None) {
2462         free(self->name);
2463         self->name = NULL;
2464     } else {
2465         char *name = copy(PyUnicode_AsUTF8(value));
2466         if (name == NULL) {
2467             return -1;
2468         }
2469         free(self->name);
2470         self->name = name;
2471     }
2472     return 0;
2473 }
2474 
2475 static PyGetSetDef PyFFContour_getset[] = {
2476     {(char *)"is_quadratic",
2477      (getter)PyFF_Contour_get_is_quadratic, (setter)PyFF_Contour_set_is_quadratic,
2478      (char *)"Whether this is an quadratic or cubic contour", NULL},
2479     {(char *)"closed",
2480      (getter)PyFF_Contour_get_closed, (setter)PyFF_Contour_set_closed,
2481      (char *)"Whether this is a closed contour", NULL},
2482     {(char *)"spiros",
2483      (getter)PyFF_Contour_get_spiros, (setter)PyFF_Contour_set_spiros,
2484      (char *)"Alternate representation of the contour as a tuple of spiro control points", NULL},
2485 /* Sigh. I misdocumented the above entry and called it "spiro" so now support both names */
2486     {(char *)"spiro",
2487      (getter)PyFF_Contour_get_spiros, (setter)PyFF_Contour_set_spiros,
2488      (char *)"Alternate representation of the contour as a tuple of spiro control points", NULL},
2489     {(char *)"name",
2490      (getter)PyFF_Contour_get_name, (setter)PyFF_Contour_set_name,
2491      (char *)"Contours may be named", NULL},
2492     PYGETSETDEF_EMPTY /* Sentinel */
2493 };
2494 
2495 /* ************************************************************************** */
2496 /* Contour sequence */
2497 /* ************************************************************************** */
PyFFContour_Length(PyObject * self)2498 static Py_ssize_t PyFFContour_Length( PyObject *self ) {
2499 return( ((PyFF_Contour *) self)->pt_cnt );
2500 }
2501 
2502 static PyFF_Contour *PyFFPointList_Parse(PyObject *args);
2503 
PyFFContour_Concat(PyObject * _c1,PyObject * _c2)2504 static PyObject *PyFFContour_Concat( PyObject *_c1, PyObject *_c2 ) {
2505     PyFF_Contour *c1 = (PyFF_Contour *) _c1, *c2 = (PyFF_Contour *)_c2;
2506     PyFF_Contour *self;
2507     int i;
2508     PyFF_Contour dummy;
2509     PyFF_Point *dummies[1];
2510     double x,y;
2511 
2512     if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(c1)) &&
2513          PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(c2)) ) {
2514 	if ( c1->is_quadratic != c2->is_quadratic ) {
2515 	    PyErr_Format(PyExc_TypeError, "Both contours must be either cubic or quadratic.");
2516 	    return( NULL );
2517 	}
2518     } else if ( ( dummies[0] = PyFFPoint_Parse(_c2, false, false) ) != NULL ) {
2519 	memset(&dummy,0,sizeof(dummy));
2520 	dummy.pt_cnt = 1;
2521 	dummy.points = dummies;
2522 	c2 = &dummy;
2523     } else if ( ( c2 = PyFFPointList_Parse(_c2) ) == NULL ) {
2524 	PyErr_Format(PyExc_TypeError, "Both arguments must be contours or encode a point or point list");
2525     }
2526 
2527     self = (PyFF_Contour *)PyFF_ContourType.tp_alloc(&PyFF_ContourType, 0);
2528     self->is_quadratic = c1->is_quadratic;
2529     self->closed = c1->closed;
2530     self->pt_max = self->pt_cnt = c1->pt_cnt + c2->pt_cnt;
2531     self->points = PyMem_New(PyFF_Point *,self->pt_max);
2532     for ( i=0; i<c1->pt_cnt; ++i ) {
2533 	Py_INCREF(c1->points[i]);
2534 	self->points[i] = c1->points[i];
2535     }
2536     for ( i=0; i<c2->pt_cnt; ++i ) {
2537 	if ( c2!=&dummy )
2538 	    Py_INCREF(c2->points[i]);
2539 	self->points[c1->pt_cnt+i] = c2->points[i];
2540     }
2541     if ( ((PyObject *)c2)!=_c2 && c2!=&dummy )
2542 	PyFFContour_dealloc(c2);
2543     Py_RETURN( (PyObject *) self );
2544 }
2545 
PyFFContour_InPlaceConcat(PyObject * _self,PyObject * _c2)2546 static PyObject *PyFFContour_InPlaceConcat( PyObject *_self, PyObject *_c2 ) {
2547     PyFF_Contour *self = (PyFF_Contour *) _self, *c2 = (PyFF_Contour *) _c2;
2548     int i, old_cnt;
2549     PyFF_Contour dummy;
2550     PyFF_Point *dummies[1];
2551     double x,y;
2552 
2553     if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(self)) &&
2554          PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(c2)) ) {
2555 	if ( self->is_quadratic != c2->is_quadratic ) {
2556 	    PyErr_Format(PyExc_TypeError, "Both contours must be either cubic or quadratic.");
2557 	    return( NULL );
2558 	}
2559     } else if ( ( dummies[0] = PyFFPoint_Parse(_c2, false, false) ) != NULL ) {
2560 	memset(&dummy,0,sizeof(dummy));
2561 	dummy.pt_cnt = 1;
2562 	dummy.points = dummies;
2563 	c2 = &dummy;
2564     } else if ( ( c2 = PyFFPointList_Parse(_c2) ) == NULL ) {
2565 	PyErr_Format(PyExc_TypeError, "Both arguments must be contours or encode a point or point list");
2566     }
2567 
2568     old_cnt = self->pt_cnt;
2569     self->pt_max = self->pt_cnt = self->pt_cnt + c2->pt_cnt;
2570     PyMem_Resize(self->points,PyFF_Point *,self->pt_max); /* Messes with self->points */
2571     for ( i=0; i<c2->pt_cnt; ++i ) {
2572 	if ( c2!=&dummy )
2573 	    Py_INCREF(c2->points[i]);
2574 	self->points[old_cnt+i] = c2->points[i];
2575     }
2576     if ( ((PyObject *)c2)!=_c2 && c2!=&dummy )
2577 	PyFFContour_dealloc(c2);
2578     PyFFContour_ClearSpiros((PyFF_Contour *) self);
2579     Py_RETURN( self );
2580 }
2581 
PyFFContour_Index(PyObject * self,Py_ssize_t pos)2582 static PyObject *PyFFContour_Index( PyObject *self, Py_ssize_t pos ) {
2583     PyFF_Contour *cont = (PyFF_Contour *) self;
2584     PyObject *ret;
2585 
2586     if ( pos<-cont->pt_cnt || pos>=cont->pt_cnt ) {
2587 	PyErr_Format(PyExc_TypeError, "Index out of bounds");
2588 	return( NULL );
2589     }
2590 
2591     if (pos < 0)
2592 	pos += cont->pt_cnt;
2593 
2594     ret = (PyObject *) cont->points[pos];
2595     Py_INCREF(ret);
2596 return( ret );
2597 }
2598 
PyFFContour_IndexAssign(PyObject * self,Py_ssize_t pos,PyObject * val)2599 static int PyFFContour_IndexAssign( PyObject *self, Py_ssize_t pos, PyObject *val ) {
2600     PyFF_Contour *cont = (PyFF_Contour *) self;
2601     PyFF_Point *old, *vpoint = NULL;
2602     int i;
2603 
2604     if ( val!=NULL && ( vpoint = PyFFPoint_Parse(val, false, false) ) == NULL ) {
2605 	PyErr_Format(PyExc_TypeError, "Unknown point format");
2606 	return( -1 );
2607     }
2608     if ( pos<-cont->pt_cnt || pos>=cont->pt_cnt ) {
2609 	PyErr_Format(PyExc_TypeError, "Index out of bounds");
2610 	return( -1 );
2611     }
2612 
2613     if (pos < 0)
2614 	pos += cont->pt_cnt;
2615 
2616     old = cont->points[pos];
2617 
2618     if ( val==NULL ) {
2619 	for ( i=pos; i<cont->pt_cnt-1; ++i )
2620 	    cont->points[i] = cont->points[i+1];
2621 	cont->pt_cnt -= 1;
2622     } else {
2623 	/* Be consistent about allowing the point array to be temporarily inconsistent
2624 	if ( cont->points[pos]->on_curve != vpoint->on_curve && !cont->is_quadratic ) {
2625 	    PyErr_Format(PyExc_TypeError, "Replacement point must have the same on_curve setting as original in a cubic contour");
2626 	    return( -1 );
2627 	}
2628 	*/
2629 	// PyFFPoint_Parse already incremented refcount
2630 	cont->points[pos] = vpoint;
2631     }
2632     PyFFContour_ClearSpiros((PyFF_Contour *) self);
2633     Py_DECREF( old );
2634     return( 0 );
2635 }
2636 
2637 
PyFFContour_Contains(PyObject * _self,PyObject * _pt)2638 static int PyFFContour_Contains(PyObject *_self, PyObject *_pt) {
2639     PyFF_Contour *self = (PyFF_Contour *) _self;
2640     double x,y;
2641     int i;
2642 
2643     if ( PySequence_Check(_pt)) {
2644 	if ( !PyArg_ParseTuple(_pt,"dd", &x, &y ))
2645 return( -1 );
2646     } else if ( !PyType_IsSubtype(&PyFF_PointType, Py_TYPE(_pt)) ) {
2647 	PyErr_Format(PyExc_TypeError, "Value must be a (FontForge) Point");
2648 return( -1 );
2649     } else {
2650 	x = ((PyFF_Point *) _pt)->x;
2651 	y = ((PyFF_Point *) _pt)->y;
2652     }
2653 
2654     for ( i=0; i<self->pt_cnt; ++i )
2655 	if ( self->points[i]->x == x && self->points[i]->y == y )
2656 return( 1 );
2657 
2658 return( 0 );
2659 }
2660 
2661 static PySequenceMethods PyFFContour_Sequence = {
2662     PyFFContour_Length,		/* length */
2663     PyFFContour_Concat,		/* concat */
2664     NULL,			/* repeat */
2665     PyFFContour_Index,		/* subscript */
2666     NULL,			/* slice */
2667     PyFFContour_IndexAssign,	/* subscript assign */
2668     NULL,			/* slice assign */
2669     PyFFContour_Contains,	/* contains */
2670     PyFFContour_InPlaceConcat,	/* inplace_concat */
2671     NULL			/* inplace repeat */
2672 };
2673 
PyFFContour_Sub(PyObject * self,PyObject * key)2674 static PyObject *PyFFContour_Sub( PyObject *self, PyObject *key ) {
2675     PyFF_Contour *cont = (PyFF_Contour *) self;
2676     PyFF_Contour *ret;
2677     Py_ssize_t start, end, step, len;
2678     int i;
2679 
2680     if ( PyLong_Check(key)) {
2681         return PyFFContour_Index(self, PyNumber_AsSsize_t(key, PyExc_IndexError));
2682     }
2683     if ( !PySlice_Check(key) ) {
2684 	PyErr_Format(PyExc_IndexError, "Contour indexed by integer only");
2685 	return( NULL );
2686     }
2687 #if PY_VERSION_HEX > 0x03060100
2688     if (PySlice_Unpack(key, &start, &end, &step) < 0) {
2689 	return( NULL );
2690     }
2691     len = PySlice_AdjustIndices(cont->pt_cnt, &start, &end, step);
2692 #elif PY_VERSION_HEX > 0x03020000
2693     if (PySlice_GetIndicesEx(key, cont->pt_cnt, &start, &end, &step, &len) < 0) {
2694 	return( NULL );
2695     }
2696 #else
2697     if (PySlice_GetIndicesEx((PySliceObject *) key, cont->pt_cnt, &start, &end, &step, &len) < 0) {
2698 	return( NULL );
2699     }
2700 #endif
2701     if ( !(step == 1 || step == -1) ) {
2702 	PyErr_Format(PyExc_IndexError, "Only supported steps are 1 and -1");
2703 	return( NULL );
2704     }
2705 
2706     ret = (PyFF_Contour *)PyFF_ContourType.tp_alloc(&PyFF_ContourType, 0);
2707     ret->is_quadratic = cont->is_quadratic;
2708     ret->closed = false;
2709     ret->pt_max = ret->pt_cnt = len;
2710     ret->points = PyMem_New(PyFF_Point *,ret->pt_max);
2711 
2712     for ( i = 0; i < len; i++ ) {
2713 	ret->points[i] = cont->points[i*step + start];
2714 	Py_INCREF(ret->points[i]);
2715     }
2716     return( (PyObject *) ret );
2717 }
2718 
2719 // Caller responsible for incrementing reference count
PyFFContour_CInsertPoint(PyFF_Contour * self,PyFF_Point * p,int pos)2720 static void PyFFContour_CInsertPoint(PyFF_Contour *self, PyFF_Point *p, int pos) {
2721 	int i;
2722 
2723 	if ( pos<0 || pos>=self->pt_cnt-1 )
2724 		pos = self->pt_cnt-1;
2725 	if ( self->pt_cnt >= self->pt_max ) {
2726 		/* Messes with self->points */
2727 		PyMem_Resize(self->points,PyFF_Point *,self->pt_max += 10);
2728 	}
2729 	for ( i=self->pt_cnt-1; i>pos; --i )
2730 	self->points[i+1] = self->points[i];
2731 	self->points[pos+1] = p;
2732 	PyFFContour_ClearSpiros(self);
2733 	self->pt_cnt += 1;
2734 }
2735 
2736 // Uses a Contour object as a temporary home for the point list
PyFFPointList_Parse(PyObject * args)2737 static PyFF_Contour *PyFFPointList_Parse(PyObject *args) {
2738 	PyFF_Contour *self = (PyFF_Contour *) PyFFContour_new(&PyFF_ContourType,NULL,NULL);
2739 	PyFF_Point *p;
2740 	int len, i;
2741 	bool ok = true;
2742 
2743 	if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(args) ) ) {
2744 		PyFFContour_InPlaceConcat((PyObject *) self, args);
2745 	} else if ( PySequence_Check(args) ) {
2746 		len = PySequence_Size(args);
2747 		for (i = 0; i < len; ++i) {
2748 			p = PyFFPoint_Parse(PySequence_GetItem(args, i), false, false);
2749 			if ( p!=NULL ) {
2750 				PyFFContour_CInsertPoint(self, p, -1);
2751 			} else {
2752 				ok = false;
2753 				break;
2754 			}
2755 		}
2756 	} else {
2757 		ok = false;
2758 	}
2759 	if ( ! ok ) {
2760 		PyFFContour_dealloc(self);
2761 		return NULL;
2762 	}
2763 	return self;
2764 }
2765 
PyFFContour_SubAssign(PyObject * self,PyObject * key,PyObject * val)2766 static int PyFFContour_SubAssign( PyObject *self, PyObject *key, PyObject *val ) {
2767     PyFF_Contour *cont = (PyFF_Contour *) self, *rpl;
2768     int i, diff;
2769     Py_ssize_t len, start, end, step;
2770 
2771     if ( PyLong_Check(key)) {
2772         return PyFFContour_IndexAssign(self, PyNumber_AsSsize_t(key, PyExc_IndexError), val);
2773     }
2774     rpl = PyFFPointList_Parse(val);
2775     if ( rpl==NULL ) {
2776 	PyErr_Format(PyExc_TypeError, "Replacement must encode a point list");
2777 	return( -1 );
2778     }
2779     if ( !PySlice_Check(key) ) {
2780 	PyErr_Format(PyExc_IndexError, "Contour indexed by integer only");
2781 	return( -1 );
2782     }
2783 #if PY_VERSION_HEX > 0x03060100
2784     if (PySlice_Unpack(key, &start, &end, &step) < 0) {
2785 	return( -1 );
2786     }
2787     len = PySlice_AdjustIndices(cont->pt_cnt, &start, &end, step);
2788 #elif PY_VERSION_HEX > 0x03020000
2789     if (PySlice_GetIndicesEx(key, cont->pt_cnt, &start, &end, &step, &len) < 0) {
2790 	return( -1 );
2791     }
2792 #else
2793     if (PySlice_GetIndicesEx((PySliceObject *) key, cont->pt_cnt, &start, &end, &step, &len) < 0) {
2794 	return( -1 );
2795     }
2796 #endif
2797     if ( !(step == 1 || step == -1) ) {
2798 	PyErr_Format(PyExc_IndexError, "Only supported steps are 1 and -1");
2799 	return( -1 );
2800     }
2801 
2802     diff = ( rpl!=NULL ? rpl->pt_cnt : 0 ) - len;
2803     for ( i=start; i<end; ++i )
2804 	Py_DECREF(cont->points[i]);
2805     if ( diff>0 ) {
2806 	if ( cont->pt_cnt+diff >= cont->pt_max ) {
2807 	    cont->pt_max = cont->pt_cnt + diff;
2808 	    PyMem_Resize(cont->points,PyFF_Point *,cont->pt_max); /* Messes with cont->points */
2809 	}
2810 	for ( i=cont->pt_cnt-1; i>=end; --i )
2811 	    cont->points[i+diff] = cont->points[i];
2812     } else if ( diff<0 ) {
2813 	for ( i=end; i<cont->pt_cnt; ++i )
2814 	    cont->points[i+diff] = cont->points[i];
2815     }
2816     cont->pt_cnt += diff;
2817     for ( i=0; i<rpl->pt_cnt; ++i ) {
2818 	cont->points[i*step+start] = rpl->points[i];
2819 	Py_INCREF(rpl->points[i]);
2820     }
2821     PyFFContour_ClearSpiros(cont);
2822     PyFFContour_dealloc(rpl);
2823     return( 0 );
2824 }
2825 
2826 static PyMappingMethods PyFFContour_Mapping = {
2827     PyFFContour_Length,			/* length */
2828     PyFFContour_Sub,			/* subscript */
2829     PyFFContour_SubAssign		/* subscript assign */
2830 };
2831 
2832 /* ************************************************************************** */
2833 /* Contour methods */
2834 /* ************************************************************************** */
PyFFContour_dup(PyFF_Contour * self)2835 static PyObject *PyFFContour_dup(PyFF_Contour *self) {
2836     /* Arg checking done elsewhere */
2837     int i;
2838     PyFF_Contour *ret = (PyFF_Contour *)PyFF_ContourType.tp_alloc(&PyFF_ContourType, 0);
2839     ret->pt_max = ret->pt_cnt = self->pt_cnt;
2840     ret->spiro_cnt = self->spiro_cnt;
2841     ret->name = copy(self->name);
2842     ret->is_quadratic = self->is_quadratic;
2843     ret->closed = self->closed;
2844     if ( ret->pt_cnt!=0 ) {
2845 	ret->points = PyMem_New(PyFF_Point *,ret->pt_cnt);
2846 	for ( i=0; i<ret->pt_cnt; ++i )
2847 	    ret->points[i] = (PyFF_Point *) PyFFPoint_dup(self->points[i]);
2848     }
2849     if ( ret->spiro_cnt!=0 ) {
2850 	ret->spiros = malloc(ret->pt_cnt*sizeof(spiro_cp));
2851 	memcpy(ret->spiros,self->spiros,self->spiro_cnt*sizeof(spiro_cp));
2852     }
2853 return( (PyObject *) ret );
2854 }
2855 
PyFFContour_IsEmpty(PyFF_Contour * self)2856 static PyObject *PyFFContour_IsEmpty(PyFF_Contour *self) {
2857     /* Arg checking done elsewhere */
2858 return( Py_BuildValue("i",self->pt_cnt==0 ) );
2859 }
2860 
PyFFContour_Start(PyFF_Contour * self,PyObject * args)2861 static PyObject *PyFFContour_Start(PyFF_Contour *self, PyObject *args) {
2862     double x,y;
2863 
2864     if ( self->pt_cnt!=0 ) {
2865 	PyErr_SetString(PyExc_AttributeError, "Contour not empty");
2866 return( NULL );
2867     }
2868     if ( !PyArg_ParseTuple( args, "dd", &x, &y ))
2869 return( NULL );
2870     if ( 1>self->pt_max ) {
2871         /* Messes with self->points */
2872         PyMem_Resize(self->points,PyFF_Point *,self->pt_max += 10);
2873     }
2874     self->points[0] = PyFFPoint_CNew(x,y,true,false,0,NULL);
2875     self->pt_cnt = 1;
2876     PyFFContour_ClearSpiros((PyFF_Contour *) self);
2877 
2878 Py_RETURN( self );
2879 }
2880 
PyFFContour_LineTo(PyFF_Contour * self,PyObject * args)2881 static PyObject *PyFFContour_LineTo(PyFF_Contour *self, PyObject *args) {
2882     double x,y;
2883     int pos = -1, i;
2884 
2885     if ( self->pt_cnt==0 ) {
2886 	PyErr_SetString(PyExc_AttributeError, "Contour empty");
2887 return( NULL );
2888     }
2889     if ( !PyArg_ParseTuple( args, "dd|i", &x, &y, &pos )) {
2890 	PyErr_Clear();
2891 	if ( !PyArg_ParseTuple( args, "(dd)|i", &x, &y, &pos ))
2892 return( NULL );
2893     }
2894     if ( pos<0 || pos>=self->pt_cnt-1 )
2895 	pos = self->pt_cnt-1;
2896     while ( pos>=0 && !self->points[pos]->on_curve )
2897 	--pos;
2898     if ( pos<0 ) {
2899 	PyErr_SetString(PyExc_AttributeError, "Contour contains no on-curve points");
2900 return( NULL );
2901     }
2902     if ( self->pt_cnt >= self->pt_max ) {
2903         /* Messes with self->points */
2904         PyMem_Resize(self->points,PyFF_Point *,self->pt_max += 10);
2905     }
2906     for ( i=self->pt_cnt-1; i>pos; --i )
2907 	self->points[i+1] = self->points[i];
2908     self->points[pos+1] = PyFFPoint_CNew(x,y,true,false,0,NULL);
2909     PyFFContour_ClearSpiros((PyFF_Contour *) self);
2910     ++self->pt_cnt;
2911 
2912 Py_RETURN( self );
2913 }
2914 
PyFFContour_CubicTo(PyFF_Contour * self,PyObject * args)2915 static PyObject *PyFFContour_CubicTo(PyFF_Contour *self, PyObject *args) {
2916     double x[3],y[3];
2917     PyFF_Point *np, *pp, *p;
2918     int pos=-1, i;
2919 
2920     if ( self->is_quadratic || self->pt_cnt==0 ) {
2921 	PyErr_SetString(PyExc_AttributeError, "Contour quadratic, or empty");
2922 return( NULL );
2923     }
2924     if ( !PyArg_ParseTuple( args, "(dd)(dd)(dd)|i", &x[0], &y[0], &x[1], &y[1], &x[2], &y[2], &pos )) {
2925 	PyErr_Clear();
2926 	if ( !PyArg_ParseTuple( args, "dddddd|i", &x[0], &y[0], &x[1], &y[1], &x[2], &y[2], &pos ))
2927 return( NULL );
2928     }
2929     np = PyFFPoint_CNew(x[0],y[0],false,false,0,NULL);
2930     pp = PyFFPoint_CNew(x[1],y[1],false,false,0,NULL);
2931     p = PyFFPoint_CNew(x[2],y[2],true,false,0,NULL);
2932     if ( p==NULL ) {
2933 	Py_XDECREF(pp);
2934 	Py_XDECREF(np);
2935 return( NULL );
2936     }
2937 
2938     if ( pos<0 || pos>=self->pt_cnt-1 )
2939 	pos = self->pt_cnt-1;
2940     while ( pos>=0 && !self->points[pos]->on_curve )
2941 	--pos;
2942     if ( pos<0 ) {
2943 	PyErr_SetString(PyExc_AttributeError, "Contour contains no on-curve points");
2944 return( NULL );
2945     }
2946     if ( self->pt_cnt+3 >= self->pt_max ) {
2947         /* Messes with self->points */
2948         PyMem_Resize(self->points,PyFF_Point *,self->pt_max += 10);
2949     }
2950     for ( i=self->pt_cnt-1; i>pos; --i )
2951 	self->points[i+3] = self->points[i];
2952     self->points[pos+1] = np;
2953     self->points[pos+2] = pp;
2954     self->points[pos+3] = p;
2955     PyFFContour_ClearSpiros((PyFF_Contour *) self);
2956     self->pt_cnt += 3;
2957 Py_RETURN( self );
2958 }
2959 
2960 
PyFFContour_QuadraticTo(PyFF_Contour * self,PyObject * args)2961 static PyObject *PyFFContour_QuadraticTo(PyFF_Contour *self, PyObject *args) {
2962     double x[2],y[2];
2963     PyFF_Point *cp, *p;
2964     int pos=-1, i;
2965 
2966     if ( !self->is_quadratic || self->pt_cnt==0 ) {
2967 	PyErr_SetString(PyExc_AttributeError, "Contour cubic, or empty");
2968 return( NULL );
2969     }
2970     if ( !PyArg_ParseTuple( args, "(dd)(dd)|i", &x[0], &y[0], &x[1], &y[1], &pos )) {
2971 	PyErr_Clear();
2972 	if ( !PyArg_ParseTuple( args, "dddd|i", &x[0], &y[0], &x[1], &y[1], &pos ))
2973 return( NULL );
2974     }
2975     cp = PyFFPoint_CNew(x[0],y[0],false,false,0,NULL);
2976     p = PyFFPoint_CNew(x[1],y[1],true,false,0,NULL);
2977     if ( p==NULL ) {
2978 	Py_XDECREF(cp);
2979 return( NULL );
2980     }
2981 
2982     if ( pos<0 || pos>=self->pt_cnt-1 )
2983 	pos = self->pt_cnt-1;
2984     while ( pos>=0 && !self->points[pos]->on_curve )
2985 	--pos;
2986     if ( pos<0 ) {
2987 	PyErr_SetString(PyExc_AttributeError, "Contour contains no on-curve points");
2988 return( NULL );
2989     }
2990     if ( self->pt_cnt+2 >= self->pt_max ) {
2991         /* Messes with self->points */
2992         PyMem_Resize(self->points,PyFF_Point *,self->pt_max += 10);
2993     }
2994     for ( i=self->pt_cnt-1; i>pos; --i )
2995 	self->points[i+2] = self->points[i];
2996     self->points[pos+1] = cp;
2997     self->points[pos+2] = p;
2998     self->pt_cnt += 2;
2999     PyFFContour_ClearSpiros((PyFF_Contour *) self);
3000 Py_RETURN( self );
3001 }
3002 
PyFFContour_InsertPoint(PyFF_Contour * self,PyObject * args)3003 static PyObject *PyFFContour_InsertPoint(PyFF_Contour *self, PyObject *args) {
3004     double x,y;
3005     PyFF_Point *p=NULL;
3006     int i, on, pos, sel, type;
3007 
3008     x = y = 0.0;
3009     pos = -1;
3010     on = true;
3011     sel = false;
3012     type = PyFF_ConvertFromPointType(pt_corner);
3013     if ( !PyArg_ParseTuple( args, "(ddiii)|i", &x, &y, &on, &type, &sel, &pos )) {
3014 	PyErr_Clear();
3015     if ( !PyArg_ParseTuple( args, "(ddii)|i", &x, &y, &on, &type, &pos )) {
3016 	PyErr_Clear();
3017 	if ( !PyArg_ParseTuple( args, "(ddi)|i", &x, &y, &on, &pos )) {
3018 	    PyErr_Clear();
3019 	    if ( !PyArg_ParseTuple( args, "(dd)|i", &x, &y, &pos )) {
3020 		PyErr_Clear();
3021 		if ( !PyArg_ParseTuple( args, "O|i", &p, &pos ) ||
3022 		     !PyType_IsSubtype(&PyFF_PointType, Py_TYPE(p)) ) {
3023 		    return( NULL );
3024 		}
3025 	    }
3026 	}
3027     }
3028     }
3029 
3030     if ( p==NULL ) {
3031 	p = PyFFPoint_CNew(x,y,on,sel,type,NULL);
3032 	if ( p==NULL )
3033 	    return( NULL );
3034     } else {
3035 	Py_INCREF( p );
3036     }
3037 
3038     PyFFContour_CInsertPoint(self, p, pos);
3039     Py_RETURN( self );
3040 }
3041 
PyFFContour_MakeFirst(PyFF_Contour * self,PyObject * args)3042 static PyObject *PyFFContour_MakeFirst(PyFF_Contour *self, PyObject *args) {
3043     int pos = -1, off;
3044     int i;
3045     PyFF_Point **temp, **old;
3046 
3047     if ( !PyArg_ParseTuple( args, "i", &pos ))
3048 	return( NULL );
3049     if ( pos<0 || pos>=self->pt_cnt ) {
3050 	PyErr_Format(PyExc_TypeError, "Position argument out of bounds");
3051 	return( NULL );
3052     }
3053     if ( ! self->points[pos]->on_curve ) {
3054 	PyErr_Format(PyExc_TypeError, "First point must be on curve");
3055 	return( NULL );
3056     }
3057 
3058     temp = PyMem_New(PyFF_Point *,self->pt_max);
3059     old = self->points;
3060     for ( i=pos; i<self->pt_cnt; ++i )
3061 	temp[i-pos] = old[i];
3062     off = i-pos;
3063     for ( i=0; i<pos; ++i )
3064 	temp[i+off] = old[i];
3065     self->points = temp;
3066     PyMem_Del(old);
3067     PyFFContour_ClearSpiros((PyFF_Contour *) self);
3068 
3069 Py_RETURN( self );
3070 }
3071 
PyFFContour_ReverseDirection(PyFF_Contour * self,PyObject * UNUSED (args))3072 static PyObject *PyFFContour_ReverseDirection(PyFF_Contour *self, PyObject *UNUSED(args)) {
3073     int i, j;
3074     PyFF_Point **temp, **old;
3075 
3076     /* Arg checking done elsewhere */
3077 
3078     temp = PyMem_New(PyFF_Point *,self->pt_max);
3079     old = self->points;
3080     if ( self->closed ) {
3081 	temp[0] = old[0];
3082 	for ( i=self->pt_cnt-1, j=1; i>0; --i, ++j )
3083 	    temp[j] = old[i];
3084     } else {
3085 	for ( i=self->pt_cnt-1, j=0; i>=0; --i, ++j )
3086 	    temp[j] = old[i];
3087     }
3088     self->points = temp;
3089     PyMem_Del(old);
3090     PyFFContour_ClearSpiros((PyFF_Contour *) self);
3091 
3092 Py_RETURN( self );
3093 }
3094 
PyFFContour_IsClockwise(PyFF_Contour * self,PyObject * UNUSED (args))3095 static PyObject *PyFFContour_IsClockwise(PyFF_Contour *self, PyObject *UNUSED(args)) {
3096     SplineSet *ss;
3097     int ret;
3098 
3099     /* Arg checking done elsewhere */
3100 
3101     ss = SSFromContour(self,NULL);
3102     if ( ss==NULL ) {
3103 	if ( PyErr_Occurred() == NULL )
3104 	    PyErr_SetString(PyExc_AttributeError, "Empty Contour");
3105 	return( NULL );
3106     }
3107     ret = SplinePointListIsClockwise(ss);
3108     SplinePointListFree(ss);
3109 return( Py_BuildValue("i",ret ) );
3110 }
3111 
PyFFContour_xBoundsAtY(PyFF_Contour * self,PyObject * args)3112 static PyObject *PyFFContour_xBoundsAtY(PyFF_Contour *self, PyObject *args) {
3113     SplineSet *ss;
3114     double y1, y2=6.023e23;
3115     bigreal xmin, xmax;
3116     int ret;
3117 
3118     if ( !PyArg_ParseTuple(args,"d|d", &y1, &y2 ))
3119 return( NULL );
3120 
3121     ss = SSFromContour(self,NULL);
3122     if ( ss==NULL )
3123 Py_RETURN_NONE;
3124 
3125     if ( y2>1e23 )
3126 	y2 = y1;
3127     ret = SSBoundsWithin(ss,y1,y2,&xmin,&xmax,1);
3128     SplinePointListFree(ss);
3129     if ( !ret )
3130 Py_RETURN_NONE;
3131 
3132 return( Py_BuildValue("(dd)",(double) xmin, (double) xmax ) );
3133 }
3134 
PyFFContour_yBoundsAtX(PyFF_Contour * self,PyObject * args)3135 static PyObject *PyFFContour_yBoundsAtX(PyFF_Contour *self, PyObject *args) {
3136     SplineSet *ss;
3137     double x1, x2=6.023e23;
3138     bigreal ymin, ymax;
3139     int ret;
3140 
3141     if ( !PyArg_ParseTuple(args,"d|d", &x1, &x2 ))
3142 return( NULL );
3143 
3144     ss = SSFromContour(self,NULL);
3145     if ( ss==NULL )
3146 Py_RETURN_NONE;
3147 
3148     if ( x2>1e23 )
3149 	x2 = x1;
3150     ret = SSBoundsWithin(ss,x1,x2,&ymin,&ymax,0);
3151     SplinePointListFree(ss);
3152     if ( !ret )
3153 Py_RETURN_NONE;
3154 
3155 return( Py_BuildValue("(dd)",(double) ymin, (double) ymax ) );
3156 }
3157 
PyFFContour_Merge(PyFF_Contour * self,PyObject * args)3158 static PyObject *PyFFContour_Merge(PyFF_Contour *self, PyObject *args) {
3159     SplineSet *ss;
3160     int i, pos;
3161 
3162     ss = SSFromContour(self,NULL);
3163     if ( ss==NULL ) {
3164 	if ( PyErr_Occurred() == NULL )
3165 	    PyErr_SetString(PyExc_AttributeError, "Empty Contour");
3166 	return( NULL );
3167     }
3168     for ( i=0; i<PySequence_Size(args); ++i ) {
3169 	pos = PyLong_AsLong(PySequence_GetItem(args,i));
3170 	if ( PyErr_Occurred())
3171 return( NULL );
3172 	SSSelectOnCurve(ss,pos);
3173     }
3174     SplineCharMerge(NULL,&ss,1);
3175     if ( ss==NULL ) {
3176 	for ( i=0; i<self->pt_cnt; ++i )
3177 	    Py_DECREF(self->points[i]);
3178 	self->pt_cnt = 0;
3179     } else {
3180 	ContourFromSS(ss,self);
3181 	SplinePointListFree(ss);
3182     }
3183     PyFFContour_ClearSpiros((PyFF_Contour *) self);
3184 Py_RETURN( self );
3185 }
3186 
3187 /* Simplify flags: see 'enum simplify_flags' in splinefont.h */
3188 struct flaglist simplifyflags[] = {
3189     { "cleanup", sf_cleanup },
3190     { "ignoreslopes", sf_ignoreslopes },
3191     { "ignoreextrema", sf_ignoreextremum },
3192     { "smoothcurves", sf_smoothcurves },
3193     { "choosehv", sf_choosehv },
3194     { "forcelines", sf_forcelines },
3195     { "nearlyhvlines", sf_nearlyhvlines },
3196     { "mergelines", sf_mergelines },
3197     { "setstarttoextremum", sf_setstart2extremum },
3198     { "setstarttoextrema",  sf_setstart2extremum }, /* Documentation error */
3199     { "removesingletonpoints", sf_rmsingletonpoints },
3200     FLAGLIST_EMPTY /* Sentinel */
3201 };
3202 
PyFFContour_selfIntersects(PyFF_Contour * self,PyObject * UNUSED (args))3203 static PyObject *PyFFContour_selfIntersects(PyFF_Contour *self, PyObject *UNUSED(args)) {
3204     SplineSet *ss;
3205     Spline *s, *s2;
3206     PyObject *ret;
3207 
3208     ss = SSFromContour(self,NULL);
3209     ret = SplineSetIntersect(ss,&s,&s2) ? Py_True : Py_False;
3210     SplinePointListFree(ss);
3211     Py_INCREF( ret );
3212 return( ret );
3213 }
3214 
PyFFContour_Simplify(PyFF_Contour * self,PyObject * args)3215 static PyObject *PyFFContour_Simplify(PyFF_Contour *self, PyObject *args) {
3216     SplineSet *ss;
3217     static struct simplifyinfo smpl = { sf_normal, 0.75, 0.2, 10, 0, 0, 0 };
3218     int i;
3219 
3220     smpl.err = 1;
3221     smpl.linefixup = 2;
3222     smpl.linelenmax = 10;
3223 
3224     ss = SSFromContour(self,NULL);
3225     if ( ss==NULL ) {
3226 	if ( PyErr_Occurred() != NULL )
3227 	    return ( NULL );
3228 	else
3229 	    Py_RETURN( self );	/* As simple as it can be */
3230     }
3231 
3232     if ( PySequence_Size(args)>=1 )
3233 	smpl.err = PyFloat_AsDouble(PySequence_GetItem(args,0));
3234     if ( !PyErr_Occurred() && PySequence_Size(args)>=2 )
3235 	smpl.flags = FlagsFromTuple( PySequence_GetItem(args,1),simplifyflags,"simplify flag");
3236     if ( !PyErr_Occurred() && PySequence_Size(args)>=3 )
3237 	smpl.tan_bounds = PyFloat_AsDouble( PySequence_GetItem(args,2));
3238     if ( !PyErr_Occurred() && PySequence_Size(args)>=4 )
3239 	smpl.linefixup = PyFloat_AsDouble( PySequence_GetItem(args,3));
3240     if ( !PyErr_Occurred() && PySequence_Size(args)>=5 )
3241 	smpl.linelenmax = PyFloat_AsDouble( PySequence_GetItem(args,4));
3242     if ( PyErr_Occurred() )
3243 return( NULL );
3244     SplinePointListSimplify(NULL,ss,&smpl);
3245     if ( ss==NULL ) {
3246 	for ( i=0; i<self->pt_cnt; ++i )
3247 	    Py_DECREF(self->points[i]);
3248 	self->pt_cnt = 0;
3249     } else {
3250 	ContourFromSS(ss,self);
3251 	SplinePointListFree(ss);
3252     }
3253 Py_RETURN( self );
3254 }
3255 
PyFFContour_Transform(PyFF_Contour * self,PyObject * args)3256 static PyObject *PyFFContour_Transform(PyFF_Contour *self, PyObject *args) {
3257     int i;
3258     double m[6];
3259 
3260     if ( !PyArg_ParseTuple(args,"(dddddd)",&m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) )
3261 return( NULL );
3262     for ( i=0; i<self->pt_cnt; ++i )
3263 	PyFF_TransformPoint(self->points[i],m);
3264     PyFFContour_ClearSpiros((PyFF_Contour *) self);
3265 Py_RETURN( self );
3266 }
3267 
PyFFContour_similar(PyFF_Contour * self,PyObject * args)3268 static PyObject *PyFFContour_similar(PyFF_Contour *self, PyObject *args) {
3269     double pt_err = -1, spline_err = -1;
3270     int ret;
3271     PyObject *other, *retO;
3272 
3273     if ( !PyArg_ParseTuple(args,"O|dd", &other, &pt_err, &spline_err) )
3274 return( NULL );
3275     if ( pt_err==-1 ) {
3276 	pt_err = .5;
3277 	spline_err = pt_err;
3278     } else if ( spline_err==-1 )
3279 	spline_err = pt_err;
3280 
3281     if ( !PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(other)) ) {
3282 	PyErr_Format(PyExc_TypeError, "Unexpected type");
3283 return( NULL );
3284     }
3285 
3286     ret = PyFFContour_docompare(self,other,pt_err,spline_err);
3287     retO = (ret&SS_NoMatch) ? Py_False : Py_True;
3288     Py_INCREF( retO );
3289 return( retO );
3290 }
3291 
PyFFContour_pickleReducer(PyFF_Contour * self,PyObject * UNUSED (args))3292 static PyObject *PyFFContour_pickleReducer(PyFF_Contour *self, PyObject *UNUSED(args)) {
3293     PyObject *reductionTuple, *argTuple;
3294     int i;
3295 
3296     if ( _new_contour==NULL )
3297 	PyFF_PickleTypesInit();
3298     reductionTuple = PyTuple_New(2);
3299     Py_INCREF(_new_contour);
3300     PyTuple_SetItem(reductionTuple,0,_new_contour);
3301     argTuple = PyTuple_New(2+self->pt_cnt);
3302     PyTuple_SetItem(reductionTuple,1,argTuple);
3303     PyTuple_SetItem(argTuple,0,Py_BuildValue("i", self->is_quadratic));
3304     PyTuple_SetItem(argTuple,1,Py_BuildValue("i", self->closed));
3305     for ( i=0; i<self->pt_cnt; ++i ) {
3306 	Py_INCREF((PyObject *)self->points[i]);
3307 	PyTuple_SetItem(argTuple,2+i,(PyObject *) self->points[i]);
3308     }
3309 return( reductionTuple );
3310 }
3311 
PyFFContour_Round(PyFF_Contour * self,PyObject * args)3312 static PyObject *PyFFContour_Round(PyFF_Contour *self, PyObject *args) {
3313     double factor=1;
3314     int i;
3315 
3316     if ( !PyArg_ParseTuple(args,"|d",&factor ) )
3317 return( NULL );
3318     for ( i=0; i<self->pt_cnt; ++i ) {
3319 	self->points[i]->x = rint( factor*(double)self->points[i]->x )/factor;
3320 	self->points[i]->y = rint( factor*(double)self->points[i]->y )/factor;
3321     }
3322     PyFFContour_ClearSpiros((PyFF_Contour *) self);
3323 Py_RETURN( self );
3324 }
3325 
PyFFContour_Cluster(PyFF_Contour * self,PyObject * args)3326 static PyObject *PyFFContour_Cluster(PyFF_Contour *self, PyObject *args) {
3327     double within = .1, max = .5;
3328     SplineChar sc;
3329     SplineSet *ss;
3330     Layer layers[2];
3331 
3332     if ( !PyArg_ParseTuple(args,"|dd", &within, &max ) )
3333 return( NULL );
3334 
3335     ss = SSFromContour(self,NULL);
3336     if ( ss==NULL ) {
3337 	if ( PyErr_Occurred() != NULL )
3338 	    return ( NULL );
3339 	else
3340 	    Py_RETURN( self );	// no points=> no clusters
3341     }
3342 
3343     memset(&sc,0,sizeof(sc));
3344     memset(layers,0,sizeof(layers));
3345     sc.layers = layers;
3346     sc.layers[ly_fore].splines = ss;
3347     sc.name = copy("nameless");
3348     SCRoundToCluster( &sc,ly_fore,false,within,max);
3349     ContourFromSS(sc.layers[ly_fore].splines,self);
3350     SplinePointListFree(sc.layers[ly_fore].splines);
3351     PyFFContour_ClearSpiros((PyFF_Contour *) self);
3352 Py_RETURN( self );
3353 }
3354 
3355 /* Add Extrema type: see 'enum ae_type' in splinefont.h */
3356 struct flaglist addextremaflags[] = {
3357     { "all", ae_all },
3358     /*{ "", ae_between_selected },  -- Not needed for python */
3359     { "only_good", ae_only_good },
3360     { "only_good_rm", ae_only_good_rm_later },
3361     FLAGLIST_EMPTY /* Sentinel */
3362 };
3363 
PyFFContour_AddExtrema(PyFF_Contour * self,PyObject * args)3364 static PyObject *PyFFContour_AddExtrema(PyFF_Contour *self, PyObject *args) {
3365     int emsize = 1000;
3366     char *flag = NULL;
3367     int ae = ae_only_good;
3368     SplineSet *ss;
3369 
3370     if ( !PyArg_ParseTuple(args,"|si", &flag, &emsize ) )
3371 return( NULL );
3372     if ( flag!=NULL ) {
3373 	ae = FlagsFromString(flag,addextremaflags,"extrema flag");
3374 	if ( ae==FLAG_UNKNOWN )
3375 return( NULL );
3376     }
3377 
3378     ss = SSFromContour(self,NULL);
3379     if ( ss==NULL ) {
3380 	if ( PyErr_Occurred() != NULL )
3381 	    return ( NULL );
3382 	else
3383 	    Py_RETURN( self );	// no points=> nothing to do
3384     }
3385     SplineSetAddExtrema(NULL,ss,ae,emsize);
3386     ContourFromSS(ss,self);
3387     SplinePointListFree(ss);
3388 Py_RETURN( self );
3389 }
3390 
PyFFContour_BoundingBox(PyFF_Contour * self,PyObject * UNUSED (args))3391 static PyObject *PyFFContour_BoundingBox(PyFF_Contour *self, PyObject *UNUSED(args)) {
3392     double xmin, xmax, ymin, ymax;
3393     int i;
3394 
3395     if ( self->pt_cnt==0 )
3396         return( Py_BuildValue("(dddd)", 0.0, 0.0, 0.0, 0.0 ));
3397 
3398     xmin = xmax = self->points[0]->x;
3399     ymin = ymax = self->points[0]->y;
3400     for ( i=1; i<self->pt_cnt; ++i ) {
3401 	if ( self->points[i]->x < xmin ) xmin = self->points[i]->x;
3402 	if ( self->points[i]->x > xmax ) xmax = self->points[i]->x;
3403 	if ( self->points[i]->y < ymin ) ymin = self->points[i]->y;
3404 	if ( self->points[i]->y > ymax ) ymax = self->points[i]->y;
3405     }
3406 return( Py_BuildValue("(dddd)", xmin, ymin, xmax, ymax ));
3407 }
3408 
PyFFContour_GetSplineAfterPoint(PyFF_Contour * self,PyObject * args)3409 static PyObject *PyFFContour_GetSplineAfterPoint(PyFF_Contour *self, PyObject *args) {
3410     int pnum,prev;
3411     BasePoint start, ncp, pcp, end;
3412     double cx,cy, bx,by;
3413 
3414     if ( !PyArg_ParseTuple(args,"i", &pnum ) )
3415 return( NULL );
3416     if ( pnum>=self->pt_cnt ) {
3417 	PyErr_Format(PyExc_ValueError, "Point index out of range" );
3418 return( NULL );
3419     }
3420     if ( self->is_quadratic ) {
3421 	if ( self->points[pnum]->on_curve ) {
3422 	    start.x = self->points[pnum]->x; start.y = self->points[pnum]->y;
3423 	    if ( ++pnum>=self->pt_cnt )
3424 		pnum = 0;
3425 	    if ( self->points[pnum]->on_curve ) {
3426 		end.x = self->points[pnum]->x; end.y = self->points[pnum]->y;
3427                 return( Py_BuildValue("((dddd)(dddd))",
3428                                         0.0, 0.0,end.x-start.x,start.x,
3429                                         0.0, 0.0,end.y-start.y,start.y ));
3430 	    } else {
3431 		ncp.x = self->points[pnum]->x; ncp.y = self->points[pnum]->y;
3432 		if ( ++pnum>=self->pt_cnt )
3433 		    pnum = 0;
3434 		if ( self->points[pnum]->on_curve ) {
3435 		    end.x = self->points[pnum]->x; end.y = self->points[pnum]->y;
3436 		} else {
3437 		    end.x = ((double)self->points[pnum]->x+ncp.x)/2; end.y = ((double)self->points[pnum]->y+ncp.y)/2;
3438 		}
3439 	    }
3440 	} else {
3441 	    ncp.x = self->points[pnum]->x; ncp.y = self->points[pnum]->y;
3442 	    if ( ( prev = pnum-1 )<0 ) prev = self->pt_cnt-1;
3443 	    if ( self->points[prev]->on_curve ) {
3444 		start.x = self->points[prev]->x; start.y = self->points[prev]->y;
3445 	    } else {
3446 		start.x = ((double)self->points[prev]->x+ncp.x)/2; start.y = ((double)self->points[prev]->y+ncp.y)/2;
3447 	    }
3448 	    if ( ++pnum>=self->pt_cnt )
3449 		pnum = 0;
3450 	    if ( self->points[pnum]->on_curve ) {
3451 		end.x = self->points[pnum]->x; end.y = self->points[pnum]->y;
3452 	    } else {
3453 		end.x = ((double)self->points[pnum]->x+ncp.x)/2; end.y = ((double)self->points[pnum]->y+ncp.y)/2;
3454 	    }
3455 	}
3456 	cx = 2*(ncp.x-start.x); cy = 2*(ncp.y-start.y);
3457         return( Py_BuildValue("((dddd)(dddd))", 0.0,end.x-start.x-cx,cx,start.x,
3458                                                 0.0,end.y-start.y-cy,cy,start.y ));
3459     } else {
3460 	if ( !self->points[pnum]->on_curve ) {
3461 	    if ( ( --pnum )<0 ) pnum = self->pt_cnt-1;
3462 	    if ( !self->points[pnum]->on_curve ) {
3463 		if ( ( --pnum )<0 ) pnum = self->pt_cnt-1;
3464 	    }
3465 	}
3466 	start.x = self->points[pnum]->x; start.y = self->points[pnum]->y;
3467 	if ( ++pnum>=self->pt_cnt ) pnum = 0;
3468 	if ( self->points[pnum]->on_curve ) {
3469 	    end.x = self->points[pnum]->x; end.y = self->points[pnum]->y;
3470 	    return( Py_BuildValue("((dddd)(dddd))", 0.0,0.0,end.x-start.x,start.x, 0.0,0.0,end.y-start.y,start.y ));
3471 	}
3472 	ncp.x = self->points[pnum]->x; ncp.y = self->points[pnum]->y;
3473 	if ( ++pnum>=self->pt_cnt ) pnum = 0;
3474 	pcp.x = self->points[pnum]->x; pcp.y = self->points[pnum]->y;
3475 	if ( ++pnum>=self->pt_cnt ) pnum = 0;
3476 	end.x = self->points[pnum]->x; end.y = self->points[pnum]->y;
3477 	cx = 3*(ncp.x-start.x); cy = 3*(ncp.y-start.y);
3478 	bx = 3*(pcp.x-ncp.x)-cx; by = 3*(pcp.y-ncp.y)-cy;
3479 return( Py_BuildValue("((dddd)(dddd))", end.x-start.x-cx-bx,bx,cx,start.x,
3480 					end.y-start.y-cy-by,by,cy,start.y ));
3481     }
3482 }
3483 
do_pycall(PyObject * obj,const char * method,PyObject * args_tuple)3484 static void do_pycall(PyObject *obj,const char *method,PyObject *args_tuple) {
3485     PyObject *func, *result;
3486 
3487     func = PyObject_GetAttrString(obj,method);	/* I hope this is right */
3488     if ( func==NULL ) {
3489         fprintf( stderr, "Failed to find %s in %s\n", method, Py_TYPENAME(obj) );
3490 	Py_DECREF(args_tuple);
3491 return;
3492     }
3493     if (!PyCallable_Check(func)) {
3494 	PyErr_Format(PyExc_TypeError, "Method, %s, is not callable", method );
3495 	Py_DECREF(args_tuple);
3496 	Py_DECREF(func);
3497 return;
3498     }
3499     result = PyEval_CallObject(func, args_tuple);
3500     Py_DECREF(args_tuple);
3501     Py_XDECREF(result);
3502     Py_DECREF(func);
3503     if ( PyErr_Occurred()!=NULL )
3504 	PyErr_Print();
3505 }
3506 
PointTuple(PyFF_Point * pt)3507 static PyObject *PointTuple(PyFF_Point *pt) {
3508     PyObject *pt_tuple = PyTuple_New(2);
3509 
3510     PyTuple_SetItem(pt_tuple,0,Py_BuildValue("d",(double)pt->x));
3511     PyTuple_SetItem(pt_tuple,1,Py_BuildValue("d",(double)pt->y));
3512 return( pt_tuple );
3513 }
3514 
PyFFContour_draw(PyFF_Contour * self,PyObject * args)3515 static PyObject *PyFFContour_draw(PyFF_Contour *self, PyObject *args) {
3516     PyObject *pen, *tuple;
3517     PyFF_Point **points;
3518     int i, start, off, last, j;
3519 
3520     if ( !PyArg_ParseTuple(args,"O", &pen ) )
3521 return( NULL );
3522     if ( self->pt_cnt<2 )
3523 Py_RETURN( self );
3524 
3525     points = self->points;
3526     /* The pen protocol demands that we start with an on-curve point */
3527     /* this means we may need to rotate our point list. */
3528     /* The pen protocol allows a contour of entirely off curve quadratic points */
3529     /*  so make a special check for that (can't occur in cubics) */
3530     for ( start=0; start<self->pt_cnt; ++start )
3531 	if ( points[start]->on_curve )
3532     break;
3533     if ( start==self->pt_cnt ) {
3534 	if ( self->is_quadratic ) {
3535 	    tuple = PyTuple_New(self->pt_cnt+1);
3536 	    for ( i=0; i<self->pt_cnt; ++i ) {
3537 		PyTuple_SetItem(tuple,i,PointTuple(points[i]));
3538 	    }
3539 	    PyTuple_SetItem(tuple,i,Py_None); Py_INCREF(Py_None);
3540 	    do_pycall(pen,"qCurveTo",tuple);
3541 	} else {
3542 	    PyErr_Format(PyExc_TypeError, "A cubic contour must have at least one oncurve point to be drawn" );
3543 return( NULL );
3544 	}
3545     } else {
3546 	if ( start!=0 ) {
3547 	    points = malloc(self->pt_cnt*sizeof(PyFF_Point *));
3548 	    for ( i=start; i<self->pt_cnt; ++i )
3549 		points[i-start] = self->points[i];
3550 	    off = self->pt_cnt - start;
3551 	    for ( i=0; i<start; ++i )
3552 		points[i+off] = self->points[i];
3553 	}
3554 
3555 	tuple = PyTuple_New(1);
3556 	PyTuple_SetItem(tuple,0,PointTuple(points[0]));
3557 	do_pycall(pen,"moveTo",tuple);
3558 	if ( PyErr_Occurred()) {
3559             free(points);
3560 return( NULL );
3561         }
3562 	last = 0;
3563 	for ( i=1; i<self->pt_cnt; ++i ) {
3564 	    if ( !points[i]->on_curve )
3565 	continue;
3566 	    if ( i-last==1 ) {
3567 		tuple = PyTuple_New(1);
3568 		PyTuple_SetItem(tuple,0,PointTuple(points[i]));
3569 		do_pycall(pen,"lineTo",tuple);
3570 	    } else if ( i-last==3 && !self->is_quadratic ) {
3571 		tuple = PyTuple_New(3);
3572 		PyTuple_SetItem(tuple,0,PointTuple(points[i-2]));
3573 		PyTuple_SetItem(tuple,1,PointTuple(points[i-1]));
3574 		PyTuple_SetItem(tuple,2,PointTuple(points[i]));
3575 		do_pycall(pen,"curveTo",tuple);
3576 	    } else if ( self->is_quadratic ) {
3577 		tuple = PyTuple_New(i-last);
3578 		for ( j=last+1; j<=i; ++j )
3579 		    PyTuple_SetItem(tuple,j-(last+1),PointTuple(points[j]));
3580 		do_pycall(pen,"qCurveTo",tuple);
3581 	    } else {
3582 		PyErr_Format(PyExc_TypeError, "Wrong number of off-curve points on a cubic contour");
3583 return( NULL );
3584 	    }
3585 	    if ( PyErr_Occurred())
3586 return( NULL );
3587 	    last = i;
3588 	}
3589 	if ( i-last==3 && !self->is_quadratic && self->closed ) {
3590 	    tuple = PyTuple_New(3);
3591 	    PyTuple_SetItem(tuple,0,PointTuple(points[i-2]));
3592 	    PyTuple_SetItem(tuple,1,PointTuple(points[i-1]));
3593 	    PyTuple_SetItem(tuple,2,PointTuple(points[0]));
3594 	    do_pycall(pen,"curveTo",tuple);
3595 	} else if ( i-last!=1 && self->is_quadratic && self->closed ) {
3596 	    tuple = PyTuple_New(i-last);
3597 	    for ( j=last+1; j<i; ++j )
3598 		PyTuple_SetItem(tuple,j-(last+1),PointTuple(points[j]));
3599 	    PyTuple_SetItem(tuple,j-(last+1),PointTuple(points[0]));
3600 	    do_pycall(pen,"qCurveTo",tuple);
3601 	}
3602     }
3603 
3604     if ( PyErr_Occurred())
3605 return( NULL );
3606 
3607     tuple = PyTuple_New(0);
3608     if ( self->closed )
3609 	do_pycall(pen,"closePath",tuple);
3610     else
3611 	do_pycall(pen,"endPath",tuple);
3612     if ( PyErr_Occurred())
3613 return( NULL );
3614 
3615 Py_RETURN( self );
3616 }
3617 
3618 static PyMethodDef PyFFContour_methods[] = {
3619     {"dup", (PyCFunction)PyFFContour_dup, METH_NOARGS,
3620 	     "Returns a deep copy of the contour." },
3621     {"isEmpty", (PyCFunction)PyFFContour_IsEmpty, METH_NOARGS,
3622 	     "Returns whether a contour contains no points" },
3623     {"moveTo", (PyCFunction)PyFFContour_Start, METH_VARARGS,
3624 	     "Place an initial point on an empty contour" },
3625     {"lineTo", (PyCFunction)PyFFContour_LineTo, METH_VARARGS,
3626 	     "Append a line to a contour (optionally specifying position)" },
3627     {"cubicTo", (PyCFunction)PyFFContour_CubicTo, METH_VARARGS,
3628 	     "Append a cubic curve to a (cubic) contour (optionally specifying position)" },
3629     {"quadraticTo", (PyCFunction)PyFFContour_QuadraticTo, METH_VARARGS,
3630 	     "Append a quadratic curve to a (quadratic) contour (optionally specifying position)" },
3631     {"insertPoint", (PyCFunction)PyFFContour_InsertPoint, METH_VARARGS,
3632 	     "Add a curve point to a contour (optionally specifying position)" },
3633     {"makeFirst", (PyCFunction)PyFFContour_MakeFirst, METH_VARARGS,
3634 	     "Rotate a contour so that the specified point is first" },
3635     {"reverseDirection", (PyCFunction)PyFFContour_ReverseDirection, METH_NOARGS,
3636 	     "Reverse a closed contour so that the second point on it is the penultimate and vice versa." },
3637     {"isClockwise", (PyCFunction)PyFFContour_IsClockwise, METH_NOARGS,
3638 	     "Determine if a contour is oriented in a clockwise direction. If the contour intersects itself the results are indeterminate." },
3639     {"xBoundsAtY", (PyCFunction)PyFFContour_xBoundsAtY, METH_VARARGS,
3640 	     "The minimum and maximum values of x attained for a given y, or returns None" },
3641     {"yBoundsAtX", (PyCFunction)PyFFContour_yBoundsAtX, METH_VARARGS,
3642 	     "The minimum and maximum values of y attained for a given x, or returns None" },
3643     {"merge", (PyCFunction)PyFFContour_Merge, METH_VARARGS,
3644 	     "Removes the specified on-curve point leaving the contour otherwise intact" },
3645     {"selfIntersects", (PyCFunction)PyFFContour_selfIntersects, METH_NOARGS,
3646 	     "Returns whether this contour intersects itself" },
3647     {"similar", (PyCFunction)PyFFContour_similar, METH_VARARGS,
3648 	     "Returns whether two contours are similar within certain error bounds." },
3649     {"simplify", (PyCFunction)PyFFContour_Simplify, METH_VARARGS,
3650 	     "Smooths a contour" },
3651     {"transform", (PyCFunction)PyFFContour_Transform, METH_VARARGS,
3652 	     "Transform a contour by a 6 element matrix." },
3653     {"addExtrema", (PyCFunction)PyFFContour_AddExtrema, METH_VARARGS,
3654 	     "Add Extrema to a contour" },
3655     {"round", (PyCFunction)PyFFContour_Round, METH_VARARGS,
3656 	     "Round points on a contour" },
3657     {"cluster", (PyCFunction)PyFFContour_Cluster, METH_VARARGS,
3658 	     "Round points on a contour" },
3659     {"boundingBox", (PyCFunction)PyFFContour_BoundingBox, METH_NOARGS,
3660 	     "Finds a bounding box for the countour (xmin,ymin,xmax,ymax)" },
3661     {"getSplineAfterPoint", (PyCFunction)PyFFContour_GetSplineAfterPoint, METH_VARARGS,
3662 	     "Returns the coordinates of two cubic splines, one for x movement, one for y.\n"
3663 	     "(Quadratic curves will have 0s for the first coordinates).\n"
3664 	     "The spline will be either the on after the specified point for on-curve points.\n"
3665 	     "or the one through the specified point for control points." },
3666     {"draw", (PyCFunction)PyFFContour_draw, METH_VARARGS,
3667 	     "Support for the \"pen\" protocol (I hope)\nhttp://just.letterror.com/ltrwiki/PenProtocol" },
3668     {"__reduce__", (PyCFunction)PyFFContour_pickleReducer, METH_NOARGS,
3669 	     "cPickle calls this routine when it wants to pickle us" },
3670     PYMETHODDEF_EMPTY  /* Sentinel */
3671 };
3672 
3673 PyTypeObject PyFF_ContourType = {
3674     PyVarObject_HEAD_INIT(NULL, 0)
3675     "fontforge.contour",       /*tp_name*/
3676     sizeof(PyFF_Contour),      /*tp_basicsize*/
3677     0,                         /*tp_itemsize*/
3678     (destructor)PyFFContour_dealloc, /*tp_dealloc*/
3679     0,                         /*tp_vectorcall_offset*/
3680     NULL,                      /*tp_getattr*/
3681     NULL,                      /*tp_setattr*/
3682     NULL,                      /*tp_reserved/tp_compare*/
3683     NULL,                      /*tp_repr*/
3684     NULL,                      /*tp_as_number*/
3685     &PyFFContour_Sequence,     /*tp_as_sequence*/
3686     &PyFFContour_Mapping,      /*tp_as_mapping*/
3687     NULL,                      /*tp_hash */
3688     NULL,                      /*tp_call*/
3689     (reprfunc)PyFFContour_Str, /*tp_str*/
3690     NULL,                      /*tp_getattro*/
3691     NULL,                      /*tp_setattro*/
3692     NULL,                      /*tp_as_buffer*/
3693     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/*tp_flags*/
3694     "fontforge Contour objects", /* tp_doc */
3695     NULL /*(traverseproc)FFContour_traverse*/,  /* tp_traverse */
3696     (inquiry)PyFFContour_clear,  /* tp_clear */
3697     (richcmpfunc)PyFFContour_richcompare, /*tp_richcompare*/
3698     0,                         /* tp_weaklistoffset */
3699     contouriter_new,           /* tp_iter */
3700     NULL,                      /* tp_iternext */
3701     PyFFContour_methods,       /* tp_methods */
3702     NULL,                      /* tp_members */
3703     PyFFContour_getset,        /* tp_getset */
3704     NULL,                      /* tp_base */
3705     NULL,                      /* tp_dict */
3706     NULL,                      /* tp_descr_get */
3707     NULL,                      /* tp_descr_set */
3708     0,                         /* tp_dictoffset */
3709     (initproc)PyFFContour_init,/* tp_init */
3710     NULL,                      /* tp_alloc */
3711     PyFFContour_new,           /* tp_new */
3712     NULL,                      /* tp_free */
3713     NULL,                      /* tp_is_gc */
3714     NULL,                      /* tp_bases */
3715     NULL,                      /* tp_mro */
3716     NULL,                      /* tp_cache */
3717     NULL,                      /* tp_subclasses */
3718     NULL,                      /* tp_weaklist */
3719     NULL,                      /* tp_del */
3720     0,                         /* tp_version_tag */
3721 };
3722 
3723 /* ************************************************************************** */
3724 /* Layer iterator type */
3725 /* ************************************************************************** */
3726 
3727 typedef struct {
3728     PyObject_HEAD
3729     int pos;
3730     PyFF_Layer *layer;
3731 } layeriterobject;
3732 static PyTypeObject PyFF_LayerIterType;
3733 
layeriter_new(PyObject * layer)3734 static PyObject *layeriter_new(PyObject *layer) {
3735     layeriterobject *li;
3736     li = PyObject_New(layeriterobject, &PyFF_LayerIterType);
3737     if (li == NULL)
3738 return NULL;
3739     li->layer = ((PyFF_Layer *) layer);
3740     Py_INCREF(layer);
3741     li->pos = 0;
3742 return (PyObject *)li;
3743 }
3744 
layeriter_dealloc(layeriterobject * li)3745 static void layeriter_dealloc(layeriterobject *li) {
3746     Py_XDECREF(li->layer);
3747     PyObject_Del(li);
3748 }
3749 
layeriter_iternext(layeriterobject * li)3750 static PyObject *layeriter_iternext(layeriterobject *li) {
3751     PyFF_Layer *layer = li->layer;
3752     PyObject *c;
3753 
3754     if ( layer == NULL)
3755 return NULL;
3756 
3757     if ( li->pos<layer->cntr_cnt ) {
3758 	c = (PyObject *) layer->contours[li->pos++];
3759 	Py_INCREF(c);
3760 return( c );
3761     }
3762 
3763 return NULL;
3764 }
3765 
3766 static PyTypeObject PyFF_LayerIterType = {
3767     PyVarObject_HEAD_INIT(NULL, 0)
3768     "fontforge.layer_iterator", /* tp_name */
3769     sizeof(layeriterobject),   /* tp_basicsize */
3770     0,                         /* tp_itemsize */
3771     /* methods */
3772     (destructor)layeriter_dealloc, /* tp_dealloc */
3773     0,                         /* tp_vectorcall_offset */
3774     NULL,                      /* tp_getattr */
3775     NULL,                      /* tp_setattr */
3776     NULL,                      /* tp_compare */
3777     NULL,                      /* tp_repr */
3778     NULL,                      /* tp_as_number */
3779     NULL,                      /* tp_as_sequence */
3780     NULL,                      /* tp_as_mapping */
3781     NULL,                      /* tp_hash */
3782     NULL,                      /* tp_call */
3783     NULL,                      /* tp_str */
3784     NULL,                      /* tp_getattro */
3785     NULL,                      /* tp_setattro */
3786     NULL,                      /* tp_as_buffer */
3787     Py_TPFLAGS_DEFAULT,        /* tp_flags */
3788     NULL,                      /* tp_doc */
3789     NULL,                      /* tp_traverse */
3790     NULL,                      /* tp_clear */
3791     NULL,                      /* tp_richcompare */
3792     0,                         /* tp_weaklistoffset */
3793     PyObject_SelfIter,         /* tp_iter */
3794     (iternextfunc)layeriter_iternext, /* tp_iternext */
3795     NULL,                      /* tp_methods */
3796     NULL,                      /* tp_members */
3797     PyFFContour_getset,        /* tp_getset */
3798     NULL,                      /* tp_base */
3799     NULL,                      /* tp_dict */
3800     NULL,                      /* tp_descr_get */
3801     NULL,                      /* tp_descr_set */
3802     0,                         /* tp_dictoffset */
3803     (initproc)PyFFContour_init,/* tp_init */
3804     NULL,                      /* tp_alloc */
3805     PyFFContour_new,           /* tp_new */
3806     NULL,                      /* tp_free */
3807     NULL,                      /* tp_is_gc */
3808     NULL,                      /* tp_bases */
3809     NULL,                      /* tp_mro */
3810     NULL,                      /* tp_cache */
3811     NULL,                      /* tp_subclasses */
3812     NULL,                      /* tp_weaklist */
3813     NULL,                      /* tp_del */
3814     0,                         /* tp_version_tag */
3815 };
3816 
3817 /* ************************************************************************** */
3818 /* Layers */
3819 /* ************************************************************************** */
3820 
PyFFLayer_clear(PyFF_Layer * self)3821 static int PyFFLayer_clear(PyFF_Layer *self) {
3822     int i;
3823 
3824     for ( i=0; i<self->cntr_cnt; ++i )
3825 	Py_DECREF(self->contours[i]);
3826     self->cntr_cnt = 0;
3827 
3828 return 0;
3829 }
3830 
PyFFLayer_dealloc(PyFF_Layer * self)3831 static void PyFFLayer_dealloc(PyFF_Layer *self) {
3832     PyFFLayer_clear(self);
3833     PyMem_Del(self->contours);
3834     Py_TYPE(self)->tp_free((PyObject*)self);
3835 }
3836 
PyFFLayer_new(PyTypeObject * type,PyObject * UNUSED (args),PyObject * UNUSED (kwds))3837 static PyObject *PyFFLayer_new(PyTypeObject *type, PyObject *UNUSED(args), PyObject *UNUSED(kwds)) {
3838     PyFF_Layer *self;
3839 
3840     self = (PyFF_Layer *)type->tp_alloc(type, 0);
3841     if ( self!=NULL ) {
3842 	self->contours = NULL;
3843 	self->cntr_cnt = self->cntr_max = 0;
3844 	self->is_quadratic = 0;
3845     }
3846 
3847 return (PyObject *)self;
3848 }
3849 
PyFFLayer_init(PyFF_Layer * self,PyObject * args,PyObject * UNUSED (kwds))3850 static int PyFFLayer_init(PyFF_Layer *self, PyObject *args, PyObject *UNUSED(kwds)) {
3851     int quad=0;
3852 
3853     if ( args!=NULL && !PyArg_ParseTuple(args, "|i", &quad))
3854 return -1;
3855 
3856     self->is_quadratic = (quad!=0);
3857 return 0;
3858 }
PyFFLayer_Str(PyFF_Layer * self)3859 static PyObject *PyFFLayer_Str(PyFF_Layer *self) {
3860     char *buffer, *pt;
3861     int cnt, i,j;
3862     PyFF_Contour *contour;
3863     PyObject *ret;
3864 
3865     cnt = 0;
3866     for ( i=0; i<self->cntr_cnt; ++i )
3867 	cnt += self->contours[i]->pt_cnt;
3868     buffer=pt=malloc(cnt*30+self->cntr_cnt*30+30);
3869     strcpy(buffer, self->is_quadratic? "<Layer(quadratic)\n":"<Layer(cubic)\n");
3870     pt = buffer+strlen(buffer);
3871     for ( i=0; i<self->cntr_cnt; ++i ) {
3872 	contour = self->contours[i];
3873 	strcpy(pt, " <Contour\n" );
3874 	pt += strlen(pt);
3875 	for ( j=0; j<contour->pt_cnt; ++j ) {
3876 	    sprintf( pt, "  (%g,%g) %s\n", (double)contour->points[j]->x, (double)contour->points[j]->y,
3877 		    contour->points[j]->on_curve ? "on" : "off" );
3878 	    pt += strlen( pt );
3879 	}
3880 	strcpy(pt," >\n");
3881 	pt += strlen(pt);
3882     }
3883     strcpy(pt,">");
3884     ret = PyUnicode_FromString( buffer );
3885     free( buffer );
3886 return( ret );
3887 }
3888 
PyFFLayer_docompare(PyFF_Layer * self,PyObject * other,double pt_err,double spline_err)3889 static int PyFFLayer_docompare(PyFF_Layer *self,PyObject *other,
3890 	double pt_err, double spline_err) {
3891     SplineSet *ss, *ss2;
3892     int ret;
3893     SplinePoint *badpoint;
3894 
3895     ss = SSFromLayer(self);
3896     if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(other)) ) {
3897 	ss2 = SSFromContour((PyFF_Contour *) other,NULL);
3898     } else if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(other)) ) {
3899 	ss2 = SSFromLayer((PyFF_Layer *) other);
3900     } else {
3901 	PyErr_Format(PyExc_TypeError, "Unexpected type");
3902 return( -1 );
3903     }
3904     ret = SSsCompare(ss,ss2,pt_err,spline_err,&badpoint);
3905     SplinePointListsFree(ss);
3906     SplinePointListsFree(ss2);
3907 return(ret);
3908 }
3909 
PyFFLayer_compare(PyFF_Layer * self,PyObject * other)3910 static int PyFFLayer_compare(PyFF_Layer *self,PyObject *other) {
3911     const double pt_err = .5, spline_err = 1;
3912     int i,j,ret;
3913 
3914     ret = PyFFLayer_docompare(self,other,pt_err,spline_err);
3915     if ( !(ret&SS_NoMatch) )
3916 return( 0 );
3917 
3918     /* There's no real ordering on these guys. Make up something that is */
3919     /*  at least consistent */
3920     if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(other)) )
3921 return( -1 );
3922 
3923     /* Ok, both are layers */
3924     if ( self->cntr_cnt < ((PyFF_Layer *) other)->cntr_cnt )
3925 return( -1 );
3926     else if ( self->cntr_cnt > ((PyFF_Layer *) other)->cntr_cnt )
3927 return( 1 );
3928     /* If there's a difference, then there must be at least one point to be */
3929     /*  different. And we only get here if there were a difference */
3930     for ( j=0; j<self->cntr_cnt; ++j ) {
3931 	PyFF_Contour *scon = self->contours[j], *ocon = ((PyFF_Layer *) other)->contours[j];
3932 	if ( scon->pt_cnt<ocon->pt_cnt )
3933 return( -1 );
3934 	else if ( scon->pt_cnt>ocon->pt_cnt )
3935 return( 1 );
3936 	for ( i=0; i<scon->pt_cnt; ++i ) {
3937 	    ret = PyFFPoint_compare(scon->points[i],(PyObject *) ocon->points[i]);
3938 	    if ( ret!=0 )
3939 return( ret );
3940 	}
3941     }
3942 
3943 return( -1 );		/* Arbitrary... but we can't get here=>all points same */
3944 }
3945 
PyFFLayer_richcompare(PyObject * a,PyObject * b,int op)3946 static PyObject *PyFFLayer_richcompare(PyObject *a, PyObject *b, int op) {
3947     return enrichened_compare((cmpfunc) PyFFLayer_compare, a, b, op);
3948 }
3949 
3950 /* ************************************************************************** */
3951 /* Layer getters/setters */
3952 /* ************************************************************************** */
PyFF_Layer_get_is_quadratic(PyFF_Layer * self,void * UNUSED (closure))3953 static PyObject *PyFF_Layer_get_is_quadratic(PyFF_Layer *self, void *UNUSED(closure)) {
3954 return( Py_BuildValue("i", self->is_quadratic ));
3955 }
3956 
PyFF_Layer_set_is_quadratic(PyFF_Layer * self,PyObject * value,void * UNUSED (closure))3957 static int PyFF_Layer_set_is_quadratic(PyFF_Layer *self,PyObject *value, void *UNUSED(closure)) {
3958     int val;
3959     SplineSet *ss, *ss2;
3960 
3961     val = PyLong_AsLong(value);
3962     if ( PyErr_Occurred()!=NULL )
3963 return( -1 );
3964 
3965     val = (val!=0);
3966     if ( val == self->is_quadratic )
3967 return( 0 );
3968     ss = SSFromLayer(self);
3969     PyFFLayer_clear(self);
3970     if ( val )
3971 	ss2 = SplineSetsTTFApprox(ss);
3972     else
3973 	ss2 = SplineSetsPSApprox(ss);
3974     SplinePointListFree(ss);
3975     self->is_quadratic = (val!=0);
3976     LayerFromSS(ss2,self);
3977     SplinePointListFree(ss2);
3978 return( 0 );
3979 }
3980 
3981 static PyGetSetDef PyFFLayer_getset[] = {
3982     {(char *)"is_quadratic",
3983      (getter)PyFF_Layer_get_is_quadratic, (setter)PyFF_Layer_set_is_quadratic,
3984      (char *)"Whether this is an quadratic or cubic layer", NULL},
3985     PYGETSETDEF_EMPTY /* Sentinel */
3986 };
3987 
3988 /* ************************************************************************** */
3989 /* Layer sequence */
3990 /* ************************************************************************** */
PyFFLayer_Length(PyObject * self)3991 static Py_ssize_t PyFFLayer_Length( PyObject *self ) {
3992 return( ((PyFF_Layer *) self)->cntr_cnt );
3993 }
3994 
PyFFLayer_Concat(PyObject * _c1,PyObject * _c2)3995 static PyObject *PyFFLayer_Concat( PyObject *_c1, PyObject *_c2 ) {
3996     PyFF_Layer *c1 = (PyFF_Layer *) _c1, *c2 = (PyFF_Layer *) _c2;
3997     PyFF_Layer *self;
3998     int i;
3999     PyFF_Layer dummy;
4000     PyFF_Contour *dummies[1];
4001 
4002     if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(c2)) &&
4003 	    c1->is_quadratic == c2->is_quadratic ) {
4004 	memset(&dummy,0,sizeof(dummy));
4005 	dummy.cntr_cnt = 1;
4006 	dummy.contours = dummies; dummies[0] = (PyFF_Contour *) _c2;
4007 	c2 = &dummy;
4008     } else if ( !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(c1)) ||
4009                 !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(c2)) ||
4010 	    c1->is_quadratic != c2->is_quadratic ) {
4011 	PyErr_Format(PyExc_TypeError, "Both arguments must be Layers of the same order");
4012 return( NULL );
4013     }
4014     self = (PyFF_Layer *)PyFF_LayerType.tp_alloc(&PyFF_LayerType, 0);
4015     self->is_quadratic = c1->is_quadratic;
4016     self->cntr_max = self->cntr_cnt = c1->cntr_cnt + c2->cntr_cnt;
4017     self->contours = PyMem_New(PyFF_Contour *,self->cntr_max);
4018     for ( i=0; i<c1->cntr_cnt; ++i ) {
4019 	Py_INCREF(c1->contours[i]);
4020 	self->contours[i] = c1->contours[i];
4021     }
4022     for ( i=0; i<c2->cntr_cnt; ++i ) {
4023 	Py_INCREF(c2->contours[i]);
4024 	self->contours[c1->cntr_cnt+i] = c2->contours[i];
4025     }
4026 Py_RETURN( self );
4027 }
4028 
PyFFLayer_InPlaceConcat(PyObject * _self,PyObject * _c2)4029 static PyObject *PyFFLayer_InPlaceConcat( PyObject *_self, PyObject *_c2 ) {
4030     PyFF_Layer *self = (PyFF_Layer *) _self, *c2 = (PyFF_Layer *) _c2;
4031     int i, old_cnt;
4032     PyFF_Layer dummy;
4033     PyFF_Contour *dummies[1];
4034 
4035     if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(c2)) &&
4036 	    self->is_quadratic == ((PyFF_Contour *) c2)->is_quadratic ) {
4037 	memset(&dummy,0,sizeof(dummy));
4038 	dummy.cntr_cnt = 1;
4039 	dummy.contours = dummies; dummies[0] = (PyFF_Contour *) _c2;
4040 	c2 = &dummy;
4041     } else if ( !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(self)) ||
4042                 !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(c2)) ||
4043 	    self->is_quadratic != c2->is_quadratic ) {
4044 	PyErr_Format(PyExc_TypeError, "Both arguments must be Layers of the same order");
4045 return( NULL );
4046     }
4047     old_cnt = self->cntr_cnt;
4048     self->cntr_cnt += c2->cntr_cnt;
4049     if ( self->cntr_cnt >= self->cntr_max ) {
4050 	self->cntr_max = self->cntr_cnt;
4051 	PyMem_Resize(self->contours,PyFF_Contour *,self->cntr_max);  /* Messes with self->contours */
4052     }
4053     for ( i=0; i<c2->cntr_cnt; ++i ) {
4054 	Py_INCREF(c2->contours[i]);
4055 	self->contours[old_cnt+i] = c2->contours[i];
4056     }
4057 Py_RETURN( self );
4058 }
4059 
PyFFLayer_Index(PyObject * self,Py_ssize_t pos)4060 static PyObject *PyFFLayer_Index( PyObject *self, Py_ssize_t pos ) {
4061     PyFF_Layer *layer = (PyFF_Layer *) self;
4062     PyObject *ret;
4063 
4064     if ( pos<0 || pos>=layer->cntr_cnt ) {
4065 	PyErr_Format(PyExc_TypeError, "Index out of bounds");
4066 return( NULL );
4067     }
4068     ret = (PyObject *) layer->contours[pos];
4069     Py_INCREF(ret);
4070 return( ret );
4071 }
4072 
PyFFLayer_IndexAssign(PyObject * self,Py_ssize_t pos,PyObject * val)4073 static int PyFFLayer_IndexAssign( PyObject *self, Py_ssize_t pos, PyObject *val ) {
4074     PyFF_Layer *layer = (PyFF_Layer *) self;
4075     PyFF_Contour *old, *contour;
4076     int i;
4077 
4078     if ( val!=NULL && !PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(val)) ) {
4079 	PyErr_Format(PyExc_TypeError, "Value must be a (FontForge) Contour");
4080 	return( -1 );
4081     }
4082     if ( pos<0 || pos>=layer->cntr_cnt ) {
4083 	PyErr_Format(PyExc_TypeError, "Index out of bounds");
4084 	return( -1 );
4085     }
4086 
4087     old = layer->contours[pos];
4088 
4089     if ( val==NULL) {
4090 	for ( i=pos; i<layer->cntr_cnt-1; ++i )
4091 	    layer->contours[i] = layer->contours[i+1];
4092 	layer->cntr_cnt -= 1;
4093     } else {
4094 	contour = (PyFF_Contour *) val;
4095 	if ( contour->is_quadratic!=layer->is_quadratic ) {
4096 	    PyErr_Format(PyExc_TypeError, "Replacement contour must have the same order as the layer");
4097 	    return( -1 );
4098 	}
4099 	layer->contours[pos] = contour;
4100 	Py_INCREF( contour );
4101     }
4102 
4103     Py_DECREF( old );
4104     return( 0 );
4105 }
4106 
4107 static PySequenceMethods PyFFLayer_Sequence = {
4108     PyFFLayer_Length,		/* length */
4109     PyFFLayer_Concat,		/* concat */
4110     NULL,			/* repeat */
4111     PyFFLayer_Index,		/* subscript */
4112     NULL,			/* slice */
4113     PyFFLayer_IndexAssign,	/* subscript assign */
4114     NULL,			/* slice assign */
4115     NULL,			/* contains */
4116     PyFFLayer_InPlaceConcat,	/* inplace_concat */
4117     NULL			/* inplace repeat */
4118 };
4119 
4120 /* ************************************************************************** */
4121 /* Layer methods */
4122 /* ************************************************************************** */
PyFFLayer_dup(PyFF_Layer * self)4123 static PyObject *PyFFLayer_dup(PyFF_Layer *self) {
4124     /* Arg checking done elsewhere */
4125     int i;
4126     PyFF_Layer *ret = (PyFF_Layer *)PyFF_LayerType.tp_alloc(&PyFF_LayerType, 0);
4127 
4128     ret->cntr_cnt = ret->cntr_max = self->cntr_cnt;
4129     ret->is_quadratic = self->is_quadratic;
4130     if ( ret->cntr_cnt!=0 ) {
4131 	ret->contours = PyMem_New(PyFF_Contour *,ret->cntr_cnt);
4132 	for ( i=0; i<ret->cntr_cnt; ++i )
4133 	    ret->contours[i] = (PyFF_Contour *) PyFFContour_dup(self->contours[i]);
4134     }
4135 return( (PyObject *) ret );
4136 }
4137 
PyFFLayer_IsEmpty(PyFF_Layer * self)4138 static PyObject *PyFFLayer_IsEmpty(PyFF_Layer *self) {
4139     /* Arg checking done elsewhere */
4140 return( Py_BuildValue("i",self->cntr_cnt==0 ) );
4141 }
4142 
PyFFLayer_selfIntersects(PyFF_Layer * self,PyObject * UNUSED (args))4143 static PyObject *PyFFLayer_selfIntersects(PyFF_Layer *self, PyObject *UNUSED(args)) {
4144     SplineSet *ss;
4145     Spline *s, *s2;
4146     PyObject *ret;
4147 
4148     ss = SSFromLayer(self);
4149     ret = SplineSetIntersect(ss,&s,&s2) ? Py_True : Py_False;
4150     SplinePointListFree(ss);
4151     Py_INCREF( ret );
4152 return( ret );
4153 }
4154 
PyFFLayer_Simplify(PyFF_Layer * self,PyObject * args)4155 static PyObject *PyFFLayer_Simplify(PyFF_Layer *self, PyObject *args) {
4156     SplineSet *ss;
4157     static struct simplifyinfo smpl = { sf_normal, 0.75, 0.2, 10, 0, 0, 0 };
4158 
4159     smpl.err = 1;
4160     smpl.linefixup = 2;
4161     smpl.linelenmax = 10;
4162 
4163     ss = SSFromLayer(self);
4164     if ( ss==NULL ) {
4165 	if ( PyErr_Occurred() != NULL )
4166 	    return ( NULL );
4167 	else
4168 	    Py_RETURN( self );	/* As simple as it can be */
4169     }
4170 
4171     if ( PySequence_Size(args)>=1 )
4172 	smpl.err = PyFloat_AsDouble(PySequence_GetItem(args,0));
4173     if ( !PyErr_Occurred() && PySequence_Size(args)>=2 )
4174 	smpl.flags = FlagsFromTuple( PySequence_GetItem(args,1),simplifyflags,"simplify flag");
4175     if ( !PyErr_Occurred() && PySequence_Size(args)>=3 )
4176 	smpl.tan_bounds = PyFloat_AsDouble( PySequence_GetItem(args,2));
4177     if ( !PyErr_Occurred() && PySequence_Size(args)>=4 )
4178 	smpl.linefixup = PyFloat_AsDouble( PySequence_GetItem(args,3));
4179     if ( !PyErr_Occurred() && PySequence_Size(args)>=5 )
4180 	smpl.linelenmax = PyFloat_AsDouble( PySequence_GetItem(args,4));
4181     if ( PyErr_Occurred() )
4182 return( NULL );
4183     ss = SplineCharSimplify(NULL,ss,&smpl);
4184     LayerFromSS(ss,self);
4185     SplinePointListsFree(ss);
4186 Py_RETURN( self );
4187 }
4188 
PyFFLayer_similar(PyFF_Layer * self,PyObject * args)4189 static PyObject *PyFFLayer_similar(PyFF_Layer *self, PyObject *args) {
4190     double pt_err = -1, spline_err = -1;
4191     int ret;
4192     PyObject *other, *retO;
4193 
4194     if ( !PyArg_ParseTuple(args,"O|dd", &other, &pt_err, &spline_err) )
4195 return( NULL );
4196     if ( pt_err==-1 ) {
4197 	pt_err = .5;
4198 	spline_err = pt_err;
4199     } else if ( spline_err==-1 )
4200 	spline_err = pt_err;
4201 
4202     if ( !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(other)) &&
4203 	 !PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(other)) ) {
4204 	PyErr_Format(PyExc_TypeError, "Unexpected type");
4205 return( NULL );
4206     }
4207 
4208     ret = PyFFLayer_docompare(self,other,pt_err,spline_err);
4209     retO = (ret&SS_NoMatch) ? Py_False : Py_True;
4210     Py_INCREF( retO );
4211 return( retO );
4212 }
4213 
PyFFLayer_Transform(PyFF_Layer * self,PyObject * args)4214 static PyObject *PyFFLayer_Transform(PyFF_Layer *self, PyObject *args) {
4215     int i, j;
4216     double m[6];
4217     PyFF_Contour *cntr;
4218 
4219     if ( !PyArg_ParseTuple(args,"(dddddd)",&m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) )
4220 return( NULL );
4221     for ( i=0; i<self->cntr_cnt; ++i ) {
4222 	cntr = self->contours[i];
4223 	for ( j=0; j<cntr->pt_cnt; ++j )
4224 	    PyFF_TransformPoint(cntr->points[j],m);
4225     }
4226 Py_RETURN( self );
4227 }
4228 
PyFFLayer_NLTransform(PyFF_Layer * self,PyObject * args)4229 static PyObject *PyFFLayer_NLTransform(PyFF_Layer *self, PyObject *args) {
4230     char *xexpr, *yexpr;
4231     SplineSet *ss;
4232 
4233     if ( !PyArg_ParseTuple(args,"ss", &xexpr, &yexpr ) )
4234 return( NULL );
4235 
4236     ss = SSFromLayer(self);
4237     if ( ss==NULL ) {
4238 	if ( PyErr_Occurred() != NULL )
4239 	    return ( NULL );
4240 	else
4241 	    Py_RETURN( self );
4242     }
4243 
4244     if ( !SSNLTrans(ss,xexpr,yexpr) ) {
4245 	PyErr_Format(PyExc_TypeError, "Unparseable expression.");
4246 	SplinePointListsFree(ss);
4247 return( NULL );
4248     }
4249 
4250     LayerFromSS(ss,self);
4251     SplinePointListsFree(ss);
4252 Py_RETURN( self );
4253 }
4254 
PyFFLayer_pickleReducer(PyFF_Layer * self,PyObject * UNUSED (args))4255 static PyObject *PyFFLayer_pickleReducer(PyFF_Layer *self, PyObject *UNUSED(args)) {
4256     PyObject *reductionTuple, *argTuple;
4257     int i;
4258 
4259     if ( _new_layer==NULL )
4260 	PyFF_PickleTypesInit();
4261     reductionTuple = PyTuple_New(2);
4262     Py_INCREF(_new_layer);
4263     PyTuple_SetItem(reductionTuple,0,_new_layer);
4264     argTuple = PyTuple_New(1+self->cntr_cnt);
4265     PyTuple_SetItem(reductionTuple,1,argTuple);
4266     PyTuple_SetItem(argTuple,0,Py_BuildValue("i", self->is_quadratic));
4267     for ( i=0; i<self->cntr_cnt; ++i ) {
4268 	Py_INCREF((PyObject *)self->contours[i]);
4269 	PyTuple_SetItem(argTuple,1+i,(PyObject *)self->contours[i]);
4270     }
4271 return( reductionTuple );
4272 }
4273 
PyFFLayer_Round(PyFF_Layer * self,PyObject * args)4274 static PyObject *PyFFLayer_Round(PyFF_Layer *self, PyObject *args) {
4275     double factor=1;
4276     int i,j;
4277     PyFF_Contour *cntr;
4278 
4279     if ( !PyArg_ParseTuple(args,"|d",&factor ) )
4280 return( NULL );
4281     for ( i=0; i<self->cntr_cnt; ++i ) {
4282 	cntr = self->contours[i];
4283 	for ( j=0; j<cntr->pt_cnt; ++j ) {
4284 	    cntr->points[j]->x = rint( factor*(double)cntr->points[j]->x )/factor;
4285 	    cntr->points[j]->y = rint( factor*(double)cntr->points[j]->y )/factor;
4286 	}
4287     }
4288 Py_RETURN( self );
4289 }
4290 
PyFFLayer_Cluster(PyFF_Layer * self,PyObject * args)4291 static PyObject *PyFFLayer_Cluster(PyFF_Layer *self, PyObject *args) {
4292     double within = .1, max = .5;
4293     SplineChar sc;
4294     Layer layers[2];
4295     SplineSet *ss;
4296 
4297     if ( !PyArg_ParseTuple(args,"|dd", &within, &max ) )
4298 return( NULL );
4299 
4300     ss = SSFromLayer(self);
4301     if ( ss==NULL ) {
4302 	if ( PyErr_Occurred() != NULL )
4303 	    return ( NULL );
4304 	else
4305 	    Py_RETURN( self ); /* no contours=> no clusters */
4306     }
4307 
4308     memset(&sc,0,sizeof(sc));
4309     memset(layers,0,sizeof(layers));
4310     sc.layers = layers;
4311     sc.layers[ly_fore].splines = ss;
4312     sc.name = copy("nameless");
4313     SCRoundToCluster( &sc,ly_fore,false,within,max);
4314     LayerFromSS(sc.layers[ly_fore].splines,self);
4315     SplinePointListsFree(sc.layers[ly_fore].splines);
4316 Py_RETURN( self );
4317 }
4318 
PyFFLayer_AddExtrema(PyFF_Layer * self,PyObject * args)4319 static PyObject *PyFFLayer_AddExtrema(PyFF_Layer *self, PyObject *args) {
4320     int emsize = 1000;
4321     char *flag = NULL;
4322     int ae = ae_only_good;
4323     SplineSet *ss;
4324 
4325     if ( !PyArg_ParseTuple(args,"|si", &flag, &emsize ) )
4326 return( NULL );
4327     if ( flag!=NULL ) {
4328 	ae = FlagsFromString(flag,addextremaflags,"extrema flag");
4329         if ( ae==FLAG_UNKNOWN )
4330 return( NULL );
4331     }
4332 
4333     ss = SSFromLayer(self);
4334     if ( ss==NULL ) {
4335 	if ( PyErr_Occurred() != NULL )
4336 	    return ( NULL );
4337 	else
4338 	    Py_RETURN( self ); // no contours=> nothing to do
4339     }
4340 
4341     SplineCharAddExtrema(NULL,ss,ae,emsize);
4342     LayerFromSS(ss,self);
4343     SplinePointListsFree(ss);
4344 Py_RETURN( self );
4345 }
4346 
NibCheck(SplineSet * nib)4347 int NibCheck(SplineSet *nib) {
4348     enum ShapeType pt;
4349     SplineSet *ss;
4350 
4351     for ( ss=nib ; ss!=NULL; ss=ss->next ) {
4352 	pt = NibIsValid(ss);
4353 	if ( pt!=Shape_Convex ) {
4354 	    PyErr_Format(PyExc_ValueError, NibShapeTypeMsg(pt));
4355 	    return false;
4356 	}
4357     }
4358     return( true );
4359 }
4360 
4361 /* Linecap type: see 'enum linecap' in splinefont.h */
4362 struct flaglist linecap[] = {
4363     { "nib", lc_nib },
4364     { "butt", lc_butt },
4365     { "round", lc_round },
4366     { "square", lc_square },
4367     FLAGLIST_EMPTY /* Sentinel */
4368 };
4369 
4370 /* Linejoin type: see 'enum linejoin' in splinefont.h */
4371 struct flaglist linejoin[] = {
4372     { "nib", lj_nib },
4373     { "miter", lj_miter },
4374     { "miterclip", lj_miterclip },
4375     { "round", lj_round },
4376     { "bevel", lj_bevel },
4377     { "arcs", lj_arcs },
4378     FLAGLIST_EMPTY /* Sentinel */
4379 };
4380 
4381 struct flaglist rmov[] = {
4382     { "layer", srmov_layer },
4383     { "contour", srmov_contour },
4384     { "none", srmov_none },
4385     FLAGLIST_EMPTY /* Sentinel */
4386 };
4387 
4388 struct flaglist sal[] = {
4389     { "svg2", sal_svg2 },
4390     { "ratio", sal_ratio },
4391     { "auto", sal_auto },
4392     FLAGLIST_EMPTY /* Sentinel */
4393 };
4394 
4395 struct flaglist strokeflags[] = {
4396     { "removeinternal", 1 },
4397     { "removeexternal", 2 },
4398     { "cleanup", 4 },
4399     FLAGLIST_EMPTY /* Sentinel */
4400 };
4401 
4402 #define STROKE_OPTKEYS "removeinternal", "removeexternal", "extrema", "simplify", "accuracy", "joinlimit", "extendcap", "jlrelative", "ecrelative", "removeoverlap", "arcsclip"
4403 #define STROKE_OPTFORMAT "$ppppdddppss"
4404 #define STROKE_OPTARGS &si->removeinternal, &si->removeexternal, &si->extrema, &si->simplify, &si->accuracy_target, &si->joinlimit, &si->extendcap, &si->jlrelative, &si->ecrelative, &rostring, &acstring
4405 
4406 static char *strokekey_circ[]
4407     = { "type", "width", "cap", "join", "angle", STROKE_OPTKEYS, NULL };
4408 static char *strokebkey_circ[]
4409     = { "type", "width", "cap", "join", "flags", NULL };
4410 static char *strokekey_ellip[]
4411     = { "type", "width", "minorwidth", "angle", "cap", "join", STROKE_OPTKEYS, NULL };
4412 static char *strokebkey_ellip[]
4413     = { "type", "width", "minorwidth", "angle", "cap", "join", "flags", NULL };
4414 static char *strokekey_rect[]
4415     = { "type", "width", "height", "angle", "cap", "join", STROKE_OPTKEYS, NULL };
4416 static char *strokebkey_rect[]
4417     = { "type", "width", "height", "angle", "flags", NULL };
4418 static char *strokekey_conv[]
4419     = { "type", "contour", "angle", "cap", "join", STROKE_OPTKEYS, NULL };
4420 static char *strokebkey_conv[]
4421     = { "type", "contour", "flags", NULL };
4422 
Stroke_Parse(StrokeInfo * si,PyObject * args,PyObject * keywds)4423 static int Stroke_Parse(StrokeInfo *si, PyObject *args, PyObject *keywds) {
4424     char *type, *cap="nib", *join="nib", *rostring="layer",  *acstring="auto";
4425     const char *str;
4426     int c, j, f, r, a, toknum;
4427     PyObject *flagtuple=NULL;
4428     PyObject *nib=NULL;
4429 
4430     if ( PyTuple_Size(args)==0 ) {
4431 	PyErr_Format(PyExc_TypeError, "Expected a name of a pen type");
4432 	return( -1 );
4433     }
4434 
4435     assert( si!=NULL );
4436     InitializeStrokeInfo(si);
4437     // Verify expected defaults
4438     assert( si->rmov==srmov_layer && si->cap==lc_nib && si->join==lj_nib );
4439 
4440     if ((str = PyUnicode_AsUTF8(PyTuple_GetItem(args, 0))) == NULL) {
4441         return -1;
4442     }
4443     if ( strcmp(str, "circular")==0 ) {
4444 	si->stroke_type = si_round;
4445 	if ( !PyArg_ParseTupleAndKeywords(args, keywds,
4446                 "sd|ssd" STROKE_OPTFORMAT, strokekey_circ, &type, &si->width,
4447 		&cap, &join, &si->penangle, STROKE_OPTARGS) ) {
4448 	    PyErr_Clear();
4449 	    if ( !PyArg_ParseTupleAndKeywords(args, keywds, "sd|ssO",
4450                 strokebkey_circ, &type, &si->width,
4451 		&cap, &join, &flagtuple) ) {
4452 		PyErr_Format(PyExc_TypeError, "Wrong parameter set for "
4453 		                              "'circular' nib type" );
4454 		return( -1 );
4455 	    }
4456 	}
4457     } else if (    strcmp(str, "eliptical")==0
4458                 || strcmp(str, "elliptical")==0 ) {
4459 	si->stroke_type = si_round;
4460 	if ( !PyArg_ParseTupleAndKeywords(args, keywds,
4461                 "sdd|dss" STROKE_OPTFORMAT, strokekey_ellip, &type,
4462 	        &si->width, &si->height, &si->penangle, &cap, &join,
4463 	        STROKE_OPTARGS) ) {
4464 	    PyErr_Clear();
4465 	    if ( !PyArg_ParseTupleAndKeywords(args, keywds, "sddd|ssO",
4466                 strokebkey_ellip, &type, &si->width, &si->height,
4467 		&si->penangle, &cap, &join, &flagtuple) ) {
4468 		PyErr_Format(PyExc_TypeError, "Wrong parameter set for "
4469 		                              "'elliptical' nib type" );
4470 		return( -1 );
4471 	    }
4472 	}
4473     } else if (    strcmp(str, "calligraphic")==0
4474                 || strcmp(str, "rectangular")==0
4475                 || strcmp(str, "caligraphic")==0
4476                 || strcmp(str, "square")==0 ) {
4477 	si->stroke_type = si_calligraphic;
4478 	if ( !PyArg_ParseTupleAndKeywords(args, keywds,
4479                 "sdd|dss" STROKE_OPTFORMAT, strokekey_rect, &type,
4480 	        &si->width, &si->height, &si->penangle, &cap, &join,
4481 	        STROKE_OPTARGS) ) {
4482 	    PyErr_Clear();
4483 	    if ( !PyArg_ParseTupleAndKeywords(args, keywds, "sddd|O",
4484                 strokebkey_rect, &type, &si->width, &si->height,
4485 		&si->penangle, &flagtuple) ) {
4486 		PyErr_Format(PyExc_TypeError, "Wrong parameter set for "
4487 		                              "'calligraphic' nib type" );
4488 		return( -1 );
4489 	    }
4490 	}
4491     } else if (    strcmp(str, "convex")==0
4492                 || strcmp(str, "poly")==0
4493                 || strcmp(str, "polygonal")==0 ) {
4494 	si->stroke_type = si_nib;
4495 	if ( !PyArg_ParseTupleAndKeywords(args, keywds,
4496                 "sO|dss" STROKE_OPTFORMAT, strokekey_conv, &type,
4497 	        &nib, &si->penangle, &cap, &join, STROKE_OPTARGS) ) {
4498 	    PyErr_Clear();
4499 	    if ( !PyArg_ParseTupleAndKeywords(args, keywds, "sO|O",
4500                 strokebkey_conv, &type, &nib, &flagtuple) ) {
4501 		PyErr_Format(PyExc_TypeError, "Wrong parameter set for "
4502 		                              "'convex' nib type" );
4503 		return( -1 );
4504 	    }
4505 	}
4506     } else {
4507 	PyErr_Format(PyExc_TypeError, "Unrecognized stroke type");
4508 	return -1;
4509     }
4510 
4511     if ( si->stroke_type==si_nib ) {
4512 	SplineSet *ss=NULL;
4513 	int start=0, order2=0;
4514 
4515 	if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(nib)) ) {
4516 	    order2 = ((PyFF_Contour *) nib)->is_quadratic;
4517 	    ss = SSFromContour((PyFF_Contour *) nib,&start);
4518             if ( ss==NULL ) {
4519 		if ( PyErr_Occurred() != NULL )
4520                     PyErr_SetString(PyExc_AttributeError, "Empty Contour");
4521                 return( -1 );
4522             }
4523 	} else if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(nib)) ) {
4524 	    order2 = ((PyFF_Layer *) nib)->is_quadratic;
4525 	    ss = SSFromLayer((PyFF_Layer *) nib);
4526             if ( ss==NULL ) {
4527 		if ( PyErr_Occurred() != NULL )
4528                     PyErr_SetString(PyExc_AttributeError, "Empty Layer");
4529                 return( -1 );
4530             }
4531 	} else if ( PyUnicode_Check(nib) ) {
4532 	    if ((str = PyUnicode_AsUTF8(nib)) == NULL) {
4533 		return -1;
4534 	    }
4535 	    toknum = PyFF_ConvexNibID(str);
4536 	    if ( toknum==-1 )
4537 		return -1;
4538 	    ss = StrokeGetConvex(toknum, true);
4539 	    if ( ss==NULL ) {
4540 		PyErr_Format(PyExc_TypeError, "Nib context empty (never initialized?)");
4541 		return -1;
4542 	    }
4543 	} else {
4544 	    PyErr_Format(PyExc_TypeError, "Second argument must be a (fontforge) Contour or Layer or a nib context identifier string");
4545 	    return( -1 );
4546 	}
4547 	if ( order2 ) {
4548 	    SplineSet *temp = SSPSApprox(ss);
4549 	    SplinePointListsFree(ss);
4550 	    ss = temp;
4551 	}
4552 	if ( !NibCheck(ss) )
4553 	    return( -1 );
4554 	si->nib = ss;
4555     } else if ( si->width<=0 || si->height<0 ) {
4556         PyErr_Format(PyExc_ValueError, "Stroke dimensions must be positive" );
4557 	return( -1 );
4558     }
4559 
4560     c = FlagsFromString(cap,linecap,"linecap type");
4561     if ( c==FLAG_UNKNOWN )
4562 	return( -1 );
4563     if ( c==lc_square ) {
4564 	c = lc_butt;
4565 	if ( si->extendcap!=0 )
4566 	    si->extendcap=0.5;
4567     }
4568     si->cap = c;
4569     j = FlagsFromString(join,linejoin,"linejoin type");
4570     if ( j==FLAG_UNKNOWN )
4571 	return( -1 );
4572     si->join = j;
4573     r = FlagsFromString(rostring,rmov,"removeoverlap type");
4574     if ( r==FLAG_UNKNOWN )
4575 	return( -1 );
4576     si->rmov = r;
4577     a = FlagsFromString(acstring,sal,"arcsclip type");
4578     if ( a==FLAG_UNKNOWN )
4579 	return( -1 );
4580     si->al = a;
4581 
4582     if ( flagtuple!=NULL ) {
4583 	f = FlagsFromTuple(flagtuple,strokeflags,"stroke flag");
4584 	if ( f==FLAG_UNKNOWN )
4585 	    return( -1 );
4586 	si->removeinternal = ( f&1!=0 );
4587 	si->removeexternal = ( f&2!=0 );
4588 	si->simplify = ( f&4!=0 );
4589     }
4590     return( 0 );
4591 }
4592 
4593 static int LayerArgToLayer(SplineFont *sf, PyObject* layerp);
4594 
4595 static char *layer_export_keywords[] = { "filename", "usetransform", "usesystem", "asksystem", NULL };
4596 
PyFFLayer_export(PyFF_Layer * self,PyObject * args,PyObject * keywds)4597 static PyObject *PyFFLayer_export(PyFF_Layer *self, PyObject *args,
4598                                   PyObject *keywds) {
4599     char *filename;
4600     char *locfilename = NULL;
4601     char *pt;
4602     FILE *file;
4603     SplineChar sc;
4604     Layer dummylayers[2];
4605     int use_system = false, ask_system = false;
4606     ExportParams ep, *epp;
4607 
4608     InitExportParams(&ep);
4609 
4610     if ( !PyArg_ParseTupleAndKeywords(args,keywds,"s|$ppp",
4611                                       layer_export_keywords,&filename,
4612                                       &ep.use_transform, &use_system,
4613                                       &ask_system) )
4614 	return( NULL );
4615     locfilename = utf82def_copy(filename);
4616 
4617     if ( use_system || ask_system ) {
4618 	epp = ExportParamsState();
4619 	if ( ask_system )
4620 	    ExportParamsDlg(epp);
4621     } else
4622 	epp = &ep;
4623 
4624     pt = strrchr(locfilename,'.');
4625     if ( pt==NULL ) pt=locfilename;
4626 
4627     file = fopen( locfilename,"wb");
4628     if ( file==NULL ) {
4629 	PyErr_SetFromErrnoWithFilename(PyExc_IOError,locfilename);
4630 	free(locfilename);
4631 return( NULL );
4632     }
4633 
4634     memset(&sc,0,sizeof(sc));
4635     memset(dummylayers,0,sizeof(dummylayers));
4636     sc.name = copy("<generic layer>");
4637     sc.layers = dummylayers;
4638     sc.layer_cnt = 2;
4639     dummylayers[ly_fore].splines = SSFromLayer(self);
4640     dummylayers[ly_fore].order2 = self->is_quadratic;
4641 
4642     if ( strcasecmp(pt,".eps")==0 || strcasecmp(pt,".ps")==0 || strcasecmp(pt,".art")==0 )
4643 	_ExportEPS(file,&sc,ly_fore,true);
4644     else if ( strcasecmp(pt,".pdf")==0 )
4645 	_ExportPDF(file,&sc,ly_fore);
4646     else if ( strcasecmp(pt,".svg")==0 )
4647 	_ExportSVG(file,&sc,ly_fore,epp);
4648     else if ( strcasecmp(pt,".glif")==0 )
4649 	_ExportGlif(file,&sc,ly_fore,3);
4650     else if ( strcasecmp(pt,".glif2")==0 )
4651 	_ExportGlif(file,&sc,ly_fore,2);
4652     else if ( strcasecmp(pt,".glif3")==0 )
4653 	_ExportGlif(file,&sc,ly_fore,3);
4654     else if ( strcasecmp(pt,".plate")==0 )
4655 	_ExportPlate(file,&sc,ly_fore);
4656     /* else if ( strcasecmp(pt,".fig")==0 )*/
4657     else {
4658 	PyErr_Format(PyExc_ValueError, "Unknown file name extension \"%s\" to export", pt );
4659 	free( locfilename );
4660 	fclose(file);
4661 	SplinePointListsFree(dummylayers[ly_fore].splines);
4662 return( NULL );
4663     }
4664     fclose(file);
4665 
4666     SplinePointListsFree(dummylayers[ly_fore].splines);
4667     free( locfilename );
4668 Py_RETURN( self );
4669 }
4670 
PyFFLayer_Stroke(PyFF_Layer * self,PyObject * args,PyObject * keywds)4671 static PyObject *PyFFLayer_Stroke(PyFF_Layer *self, PyObject *args, PyObject *keywds) {
4672     SplineSet *ss, *newss;
4673     StrokeInfo si;
4674 
4675     if ( Stroke_Parse(&si, args, keywds)==-1 )
4676 	return( NULL );
4677 
4678     ss = SSFromLayer(self);
4679     if ( ss==NULL ) {
4680 	if ( PyErr_Occurred() != NULL )
4681 	    return ( NULL );
4682 	else
4683 	    Py_RETURN( self ); // no contours=> nothing to do
4684     }
4685     newss = SplineSetStroke(ss,&si,self->is_quadratic);
4686     SplinePointListFree(ss);
4687     LayerFromSS(newss,self);
4688     SplinePointListsFree(newss);
4689     SplinePointListsFree(si.nib); si.nib = NULL;
4690     Py_RETURN( self );
4691 }
4692 
PyFFLayer_Correct(PyFF_Layer * self,PyObject * UNUSED (args))4693 static PyObject *PyFFLayer_Correct(PyFF_Layer *self, PyObject *UNUSED(args)) {
4694     SplineSet *ss, *newss;
4695     int changed = false;
4696 
4697     ss = SSFromLayer(self);
4698     if ( ss==NULL ) {
4699 	if ( PyErr_Occurred() != NULL )
4700 	    return ( NULL );
4701 	else
4702 	    Py_RETURN( self ); // no contours=> nothing to do
4703     }
4704     newss = SplineSetsCorrect(ss,&changed);
4705     /* same old splinesets */
4706     LayerFromSS(newss,self);
4707     SplinePointListsFree(newss);
4708 Py_RETURN( self );
4709 }
4710 
PyFFLayer_ReverseDirection(PyFF_Layer * self,PyObject * UNUSED (args))4711 static PyObject *PyFFLayer_ReverseDirection(PyFF_Layer *self, PyObject *UNUSED(args)) {
4712     int i;
4713 
4714     for ( i=0; i<self->cntr_cnt; ++i )
4715 	PyFFContour_ReverseDirection(self->contours[i],NULL);
4716 Py_RETURN( self );
4717 }
4718 
PyFFLayer_RemoveOverlap(PyFF_Layer * self,PyObject * UNUSED (args))4719 static PyObject *PyFFLayer_RemoveOverlap(PyFF_Layer *self, PyObject *UNUSED(args)) {
4720     SplineSet *ss, *newss;
4721 
4722     ss = SSFromLayer(self);
4723     if ( ss==NULL ) {
4724 	if ( PyErr_Occurred() != NULL )
4725 	    return ( NULL );
4726 	else
4727 	    Py_RETURN( self ); // no contours=> nothing to do
4728     }
4729     newss = SplineSetRemoveOverlap(NULL,ss,over_remove);
4730     /* Frees the old splinesets */
4731     LayerFromSS(newss,self);
4732     SplinePointListsFree(newss);
4733 Py_RETURN( self );
4734 }
4735 
PyFFLayer_Intersect(PyFF_Layer * self,PyObject * UNUSED (args))4736 static PyObject *PyFFLayer_Intersect(PyFF_Layer *self, PyObject *UNUSED(args)) {
4737     SplineSet *ss, *newss;
4738 
4739     ss = SSFromLayer(self);
4740     if ( ss==NULL ) {
4741 	if ( PyErr_Occurred() != NULL )
4742 	    return ( NULL );
4743 	else
4744 	    Py_RETURN( self ); // no contours=> nothing to do
4745     }
4746     newss = SplineSetRemoveOverlap(NULL,ss,over_intersect);
4747     /* Frees the old splinesets */
4748     LayerFromSS(newss,self);
4749     SplinePointListsFree(newss);
4750 Py_RETURN( self );
4751 }
4752 
PyFFLayer_Exclude(PyFF_Layer * self,PyObject * args)4753 static PyObject *PyFFLayer_Exclude(PyFF_Layer *self, PyObject *args) {
4754     SplineSet *ss, *newss, *excludes, *tail;
4755     PyObject *obj;
4756 
4757     if ( !PyArg_ParseTuple(args,"O", &obj ) )
4758 return( NULL );
4759     if ( !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(obj)) ) {
4760 	PyErr_Format(PyExc_TypeError, "Value must be a (FontForge) Layer");
4761 return( NULL );
4762     }
4763 
4764     ss = SSFromLayer(self);
4765     if ( ss==NULL ) {
4766 	if ( PyErr_Occurred() != NULL )
4767 	    return ( NULL );
4768 	else
4769 	    Py_RETURN( self ); // no contours=> nothing to do
4770     }
4771     excludes = SSFromLayer((PyFF_Layer *) obj);
4772     for ( tail=ss; tail->next!=NULL; tail=tail->next );
4773     tail->next = excludes;
4774     while ( excludes!=NULL ) {
4775 	excludes->first->selected = true;
4776 	excludes = excludes->next;
4777     }
4778     newss = SplineSetRemoveOverlap(NULL,ss,over_exclude);
4779     /* Frees the old splinesets */
4780     LayerFromSS(newss,self);
4781     SplinePointListsFree(newss);
4782 Py_RETURN( self );
4783 }
4784 
PyFFLayer_interpolateNewLayer(PyFF_Layer * self,PyObject * args)4785 static PyObject *PyFFLayer_interpolateNewLayer(PyFF_Layer *self, PyObject *args) {
4786     double amount;
4787     PyObject *obj;
4788     SplineSet *ss, *otherss, *newss;
4789     SplineChar dummy;
4790 
4791     if ( !PyArg_ParseTuple(args,"Od", &obj, &amount ) )
4792 return( NULL );
4793     if ( !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(obj)) ) {
4794 	PyErr_Format(PyExc_TypeError, "Value must be a (FontForge) Layer");
4795 return( NULL );
4796     }
4797 
4798     memset(&dummy,0,sizeof(dummy));		/* Only used for error messages */
4799     dummy.name = _("<no glyph>");
4800 
4801     ss = SSFromLayer(self);
4802     otherss = SSFromLayer((PyFF_Layer *) obj);
4803     newss = SplineSetsInterpolate(ss,otherss,amount,&dummy);
4804     obj = (PyObject *) LayerFromSS(newss,NULL);
4805     SplinePointListsFree(ss);
4806     SplinePointListsFree(otherss);
4807     SplinePointListsFree(newss);
4808 return( obj );
4809 }
4810 
PyFFLayer_stemControl(PyObject * self,PyObject * args)4811 static PyObject *PyFFLayer_stemControl(PyObject *self, PyObject *args) {
4812     SplineSet *ss;
4813     double stemwidthscale, stemheightscale= -1, vscale= -1, hscale=1, xheight=-1;
4814 
4815     if ( !PyArg_ParseTuple(args,"d|dddd", &stemwidthscale, &hscale, &stemheightscale, &vscale, &xheight ) )
4816 return( NULL );
4817 
4818     ss = SSFromLayer((PyFF_Layer *) self);
4819     if ( ss==NULL ) {
4820 	if ( PyErr_Occurred() != NULL )
4821 	    return ( NULL );
4822 	else
4823 	    Py_RETURN( self ); // no contours=> nothing to do
4824     }
4825     ss = SSControlStems(ss,stemwidthscale,stemheightscale,hscale,vscale,xheight);
4826     LayerFromSS(ss,(PyFF_Layer *) self);
4827     SplinePointListsFree(ss);
4828 Py_RETURN( self );
4829 }
4830 
PyFFLayer_BoundingBox(PyFF_Layer * self,PyObject * UNUSED (args))4831 static PyObject *PyFFLayer_BoundingBox(PyFF_Layer *self, PyObject *UNUSED(args)) {
4832     double xmin, xmax, ymin, ymax;
4833     int i,j,none;
4834     PyFF_Contour *cntr;
4835 
4836     none = true;
4837     for ( j=0; j<self->cntr_cnt; ++j ) {
4838 	cntr = self->contours[j];
4839 	for ( i=0; i<cntr->pt_cnt; ++i ) {
4840 	    if ( none ) {
4841 		xmin = xmax = cntr->points[0]->x;
4842 		ymin = ymax = cntr->points[0]->y;
4843 		none = false;
4844 	    } else {
4845 		if ( cntr->points[i]->x < xmin ) xmin = cntr->points[i]->x;
4846 		if ( cntr->points[i]->x > xmax ) xmax = cntr->points[i]->x;
4847 		if ( cntr->points[i]->y < ymin ) ymin = cntr->points[i]->y;
4848 		if ( cntr->points[i]->y > ymax ) ymax = cntr->points[i]->y;
4849 	    }
4850 	}
4851     }
4852     if ( none )
4853 return( Py_BuildValue("(dddd)", 0.0,0.0,0.0,0.0 ));
4854 
4855 return( Py_BuildValue("(dddd)", xmin, ymin, xmax, ymax ));
4856 }
4857 
PyFFLayer_xBoundsAtY(PyFF_Layer * self,PyObject * args)4858 static PyObject *PyFFLayer_xBoundsAtY(PyFF_Layer *self, PyObject *args) {
4859     SplineSet *ss;
4860     double y1, y2=6.023e23;
4861     int ret;
4862     bigreal xmin, xmax;
4863 
4864     if ( !PyArg_ParseTuple(args,"d|d", &y1, &y2 ))
4865 return( NULL );
4866 
4867     ss = SSFromLayer((PyFF_Layer *) self);
4868     if ( ss==NULL )
4869 Py_RETURN_NONE;
4870 
4871     if ( y2>1e23 )
4872 	y2 = y1;
4873     ret = SSBoundsWithin(ss,y1,y2,&xmin,&xmax,1);
4874     SplinePointListsFree(ss);
4875     if ( !ret )
4876 Py_RETURN_NONE;
4877 
4878 return( Py_BuildValue("(dd)",(double) xmin, (double) xmax ) );
4879 }
4880 
PyFFLayer_yBoundsAtX(PyFF_Layer * self,PyObject * args)4881 static PyObject *PyFFLayer_yBoundsAtX(PyFF_Layer *self, PyObject *args) {
4882     SplineSet *ss;
4883     double x1, x2=6.023e23;
4884     int ret;
4885     bigreal ymin, ymax;
4886 
4887     if ( !PyArg_ParseTuple(args,"d|d", &x1, &x2 ))
4888 return( NULL );
4889 
4890     ss = SSFromLayer((PyFF_Layer *) self);
4891     if ( ss==NULL )
4892 Py_RETURN_NONE;
4893 
4894     if ( x2>1e23 )
4895 	x2 = x1;
4896     ret = SSBoundsWithin(ss,x1,x2,&ymin,&ymax,0);
4897     SplinePointListsFree(ss);
4898     if ( !ret )
4899 Py_RETURN_NONE;
4900 
4901 return( Py_BuildValue("(dd)",(double) ymin, (double) ymax ) );
4902 }
4903 
PyFFLayer_draw(PyFF_Layer * self,PyObject * args)4904 static PyObject *PyFFLayer_draw(PyFF_Layer *self, PyObject *args) {
4905     int i;
4906 
4907     for ( i=0; i<self->cntr_cnt; ++i )
4908 	PyFFContour_draw(self->contours[i],args);
4909 Py_RETURN( self );
4910 }
4911 
4912 static PyMethodDef PyFFLayer_methods[] = {
4913     {"dup", (PyCFunction)PyFFLayer_dup, METH_NOARGS,
4914 	     "Returns a deep copy of the layer" },
4915     {"isEmpty", (PyCFunction)PyFFLayer_IsEmpty, METH_NOARGS,
4916 	     "Returns whether a layer contains no contours" },
4917     {"similar", (PyCFunction)PyFFLayer_similar, METH_VARARGS,
4918 	     "compares two layers" },
4919     {"simplify", (PyCFunction)PyFFLayer_Simplify, METH_VARARGS,
4920 	     "Smooths a layer" },
4921     {"selfIntersects", (PyCFunction)PyFFLayer_selfIntersects, METH_NOARGS,
4922 	     "Returns whether this layer intersects itself" },
4923     {"transform", (PyCFunction)PyFFLayer_Transform, METH_VARARGS,
4924 	     "Transform a layer by a 6 element matrix." },
4925     { "nltransform", (PyCFunction)PyFFLayer_NLTransform, METH_VARARGS,
4926 	    "Transform a glyph by two non-linear expressions (one for x, one for y)." },
4927     {"addExtrema", (PyCFunction)PyFFLayer_AddExtrema, METH_VARARGS,
4928 	     "Add Extrema to a layer" },
4929     {"round", (PyCFunction)PyFFLayer_Round, METH_VARARGS,
4930 	     "Round contours on a layer" },
4931     {"cluster", (PyCFunction)PyFFLayer_Cluster, METH_VARARGS,
4932 	     "Round contours on a layer" },
4933     {"interpolateNewLayer", (PyCFunction)PyFFLayer_interpolateNewLayer, METH_VARARGS,
4934 	     "Creates a new layer by interpolating between this one and the one in the first argument" },
4935     {"boundingBox", (PyCFunction)PyFFLayer_BoundingBox, METH_NOARGS,
4936 	     "Finds a bounding box for the layer (xmin,ymin,xmax,ymax)" },
4937     {"xBoundsAtY", (PyCFunction)PyFFLayer_xBoundsAtY, METH_VARARGS,
4938 	     "The minimum and maximum values of x attained for a given y, or returns None" },
4939     {"yBoundsAtX", (PyCFunction)PyFFLayer_yBoundsAtX, METH_VARARGS,
4940 	     "The minimum and maximum values of y attained for a given x, or returns None" },
4941     {"correctDirection", (PyCFunction)PyFFLayer_Correct, METH_NOARGS,
4942 	     "Orient a layer so that external contours are clockwise and internal counter clockwise." },
4943     {"reverseDirection", (PyCFunction)PyFFLayer_ReverseDirection, METH_NOARGS,
4944 	     "Reverse the orientation of each contour in the layer." },
4945     {"export", (PyCFunction)PyFFLayer_export, METH_VARARGS | METH_KEYWORDS,
4946 	     "Exports the layer to a file" },
4947     {"stroke", (PyCFunction)PyFFLayer_Stroke, METH_VARARGS | METH_KEYWORDS,
4948 	     "Strokes the contours in a layer" },
4949     {"removeOverlap", (PyCFunction)PyFFLayer_RemoveOverlap, METH_NOARGS,
4950 	     "Remove overlapping areas from a layer." },
4951     {"intersect", (PyCFunction)PyFFLayer_Intersect, METH_NOARGS,
4952 	     "Leaves the areas where the contours of a layer overlap." },
4953     {"exclude", (PyCFunction)PyFFLayer_Exclude, METH_VARARGS,
4954 	     "Exclude the area of the argument (also a layer) from the current layer" },
4955     {"stemControl", (PyCFunction)PyFFLayer_stemControl, METH_VARARGS,
4956 	     "Allows you to scale stems and counters differently" },
4957     {"draw", (PyCFunction)PyFFLayer_draw, METH_VARARGS,
4958 	     "Support for the \"pen\" protocol (I hope)\nhttp://just.letterror.com/ltrwiki/PenProtocol" },
4959     {"__reduce__", (PyCFunction)PyFFLayer_pickleReducer, METH_NOARGS,
4960 	     "cPickle calls this routine when it wants to pickle us" },
4961     PYMETHODDEF_EMPTY /* Sentinel */
4962 };
4963 
4964 PyTypeObject PyFF_LayerType = {
4965     PyVarObject_HEAD_INIT(NULL, 0)
4966     "fontforge.layer",         /* tp_name */
4967     sizeof(PyFF_Layer),        /* tp_basicsize */
4968     0,                         /* tp_itemsize */
4969     (destructor)PyFFLayer_dealloc, /* tp_dealloc */
4970     0,                         /* tp_vectorcall_offset */
4971     NULL,                      /* tp_getattr */
4972     NULL,                      /* tp_setattr */
4973     NULL,                      /* tp_reserved/tp_compare */
4974     NULL,                      /* tp_repr */
4975     NULL,                      /* tp_as_number */
4976     &PyFFLayer_Sequence,       /* tp_as_sequence */
4977     NULL,                      /* tp_as_mapping */
4978     NULL,                      /* tp_hash */
4979     NULL,                      /* tp_call */
4980     (reprfunc) PyFFLayer_Str,  /* tp_str */
4981     NULL,                      /* tp_getattro */
4982     NULL,                      /* tp_setattro */
4983     NULL,                      /* tp_as_buffer */
4984     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,/* tp_flags */
4985     "fontforge Layer objects", /* tp_doc */
4986     NULL /*(traverseproc)FFLayer_traverse*/,  /* tp_traverse */
4987     (inquiry)PyFFLayer_clear,  /* tp_clear */
4988     (richcmpfunc)PyFFLayer_richcompare, /* tp_richcompare */
4989     0,                         /* tp_weaklistoffset */
4990     layeriter_new,             /* tp_iter */
4991     NULL,                      /* tp_iternext */
4992     PyFFLayer_methods,         /* tp_methods */
4993     NULL,                      /* tp_members */
4994     PyFFLayer_getset,          /* tp_getset */
4995     NULL,                      /* tp_base */
4996     NULL,                      /* tp_dict */
4997     NULL,                      /* tp_descr_get */
4998     NULL,                      /* tp_descr_set */
4999     0,                         /* tp_dictoffset */
5000     (initproc)PyFFLayer_init,  /* tp_init */
5001     NULL,                      /* tp_alloc */
5002     PyFFLayer_new,             /* tp_new */
5003     NULL,                      /* tp_free */
5004     NULL,                      /* tp_is_gc */
5005     NULL,                      /* tp_bases */
5006     NULL,                      /* tp_mro */
5007     NULL,                      /* tp_cache */
5008     NULL,                      /* tp_subclasses */
5009     NULL,                      /* tp_weaklist */
5010     NULL,                      /* tp_del */
5011     0,                         /* tp_version_tag */
5012 };
5013 
5014 /* ************************************************************************** */
5015 /* Conversion routines between SplineSets and Contours & Layers */
5016 /* ************************************************************************** */
SSSelectOnCurve(SplineSet * ss,int pos)5017 static int SSSelectOnCurve(SplineSet *ss,int pos) {
5018     SplinePoint *sp;
5019 
5020     while ( ss!=NULL ) {
5021 	for ( sp=ss->first ; ; ) {
5022 	    if ( sp->ttfindex == pos ) {
5023 		sp->selected = true;
5024 return( true );
5025 	    }
5026 	    if ( sp->next==NULL )
5027 	break;
5028 	    sp = sp->next->to;
5029 	    if ( sp == ss->first )
5030 	break;
5031 	}
5032 	ss = ss->next;
5033     }
5034 return( 0 );
5035 }
5036 
5037 /* Point convertion flags: see 'enum pconvert_flags' in splinefont.h */
5038 struct flaglist pconvertflags[] = {
5039     { "select_none", pconvert_flag_none },
5040     { "select_all", pconvert_flag_all },
5041     { "select_smooth", pconvert_flag_smooth },
5042     { "select_incompat", pconvert_flag_incompat },
5043     { "by_geom", pconvert_flag_by_geom },
5044     { "force", pconvert_flag_force_type },
5045     { "downgrade", pconvert_flag_downgrade },
5046     { "check", pconvert_flag_check_compat },
5047     { "hvcurve", pconvert_flag_hvcurve },
5048     FLAGLIST_EMPTY /* Sentinel */
5049 };
5050 
CheckPConvertFlags(int flags,int defaults)5051 static int CheckPConvertFlags(int flags, int defaults) {
5052 	const int sels = pconvert_flag_none|pconvert_flag_all|pconvert_flag_smooth;
5053 	const int modes = pconvert_flag_by_geom|pconvert_flag_force_type
5054 	                  |pconvert_flag_downgrade|pconvert_flag_check_compat;
5055 	int defsel = defaults&sels, defmode = defaults&modes, sel, mode;
5056 
5057 	sel = flags&sels;
5058 	if ( sel==0 )
5059 		flags |= defsel;
5060 	else if ( ( sel & (sel-1) ) != 0 ) {
5061 		PyErr_Format(PyExc_ValueError, "At most one point selection flag is allowed.");
5062 		return -1;
5063 	}
5064 
5065 	mode = flags&modes;
5066 	if ( mode==0 )
5067 		flags |= defmode; // Harmless if sel == pconvert_flag_none
5068 	else if ( ( mode & (mode-1) ) != 0 ) {
5069 		PyErr_Format(PyExc_ValueError, "At most one point conversion mode flag is allowed.");
5070 		return -1;
5071 	}
5072 	// Don't worry about extraneous bits for now.
5073 	return flags;
5074 }
5075 
5076 // Will return NULL on error or empty contour, can check python error
5077 // status to tell which.
_SSFromContour(PyFF_Contour * c,int * tt_start,int flags)5078 static SplineSet *_SSFromContour(PyFF_Contour *c,int *tt_start, int flags) {
5079     int start = 0, next;
5080     int i, skipped=0, index, ok;
5081     int nexti, previ;
5082     SplineSet *ss;
5083     SplinePoint *sp;
5084 
5085     if ( c->pt_cnt==0 ) {
5086 	/*PyErr_Format(PyExc_TypeError, "Empty contour");*/
5087 return( NULL );
5088     }
5089 
5090     if ( tt_start!=NULL )
5091 	start = *tt_start;
5092     i = 0;
5093     next = start;
5094 
5095     ss = chunkalloc(sizeof(SplineSet));
5096     if ( ss==NULL )
5097 	return( NULL );
5098     if ( c->spiro_cnt!=0 ) {
5099 	ss->spiro_cnt = ss->spiro_max = c->spiro_cnt;
5100 	ss->spiros = SpiroCPCopy(c->spiros,NULL);
5101     }
5102     ss->contour_name = copy(c->name);
5103 
5104     if ( c->is_quadratic ) {
5105 	if ( !c->points[0]->on_curve ) {
5106 	    if ( c->pt_cnt==1 ) {
5107 		// XXX One off-curve point -- shouldn't this throw an error?
5108 		ss->first = ss->last = SplinePointCreate(c->points[0]->x,c->points[0]->y);
5109 		ss->start_offset = 0;
5110 		ss->first->selected = c->points[0]->selected;
5111 		ss->first->pointtype = PyFF_ConvertToPointType(c->points[0]->type);
5112 		ss->first->name = copy(c->points[0]->name);
5113 		ok = _SPLCategorizePoints(ss, flags);
5114 		if ( !ok ) {
5115 		    SplinePointListsFree(ss);
5116 		    // assert ( flags&pconvert_flag_check );
5117 		    PyErr_Format(PyExc_TypeError, "At least one point has a geometry incompatible with its type");
5118 		    return( NULL );
5119 		}
5120 		return( ss );
5121 	    }
5122 	    ++i;
5123 	    ++next;
5124 	    skipped = true;
5125 	}
5126 	while ( i<c->pt_cnt ) {
5127 	    if ( c->points[i]->on_curve ) {
5128 		sp = SplinePointCreate(c->points[i]->x,c->points[i]->y);
5129 		sp->pointtype = PyFF_ConvertToPointType(c->points[i]->type);
5130 		sp->selected = c->points[i]->selected;
5131 		sp->name = copy(c->points[i]->name);
5132 		sp->ttfindex = next++;
5133 		index = -1;
5134 		if ( i>0 && !c->points[i-1]->on_curve )
5135 		    index = i-1;
5136 		else if ( i==0 && !c->points[c->pt_cnt-1]->on_curve )
5137 		    index = c->pt_cnt-1;
5138 		if ( index!=-1 ) {
5139 		    sp->prevcp.x = c->points[index]->x;
5140 		    sp->prevcp.y = c->points[index]->y;
5141 		    sp->prevcpselected = c->points[index]->selected;
5142 		    sp->noprevcp = false;
5143 		}
5144 		if ( ss->last==NULL ) {
5145 		    ss->first = sp;
5146 		    ss->start_offset = 0;
5147 		} else
5148 		    SplineMake2(ss->last,sp);
5149 		ss->last = sp;
5150 	    } else {
5151 		if ( !c->points[i-1]->on_curve ) {
5152 		    sp = SplinePointCreate((c->points[i]->x+c->points[i-1]->x)/2,(c->points[i]->y+c->points[i-1]->y)/2);
5153 		    sp->pointtype = PyFF_ConvertToPointType(c->points[i]->type);
5154 		    sp->ttfindex = -1;
5155 		    sp->prevcp.x = c->points[i-1]->x;
5156 		    sp->prevcp.y = c->points[i-1]->y;
5157 		    sp->prevcpselected = c->points[i-1]->selected;
5158 		    sp->noprevcp = false;
5159 		    if ( ss->last==NULL ) {
5160 			ss->first = sp;
5161 			ss->start_offset = 0;
5162 		    } else
5163 			SplineMake2(ss->last,sp);
5164 		    ss->last = sp;
5165 		}
5166 		ss->last->nextcp.x = c->points[i]->x;
5167 		ss->last->nextcp.y = c->points[i]->y;
5168 		ss->last->nextcpselected = c->points[i]->selected;
5169 		ss->last->nonextcp = false;
5170 		ss->last->nextcpindex = next++;
5171 	    }
5172 	    ++i;
5173 	}
5174 	if ( skipped ) {
5175 	    i = c->pt_cnt;
5176 	    if ( !c->points[i-1]->on_curve ) {
5177 		sp = SplinePointCreate((c->points[0]->x+c->points[i-1]->x)/2,(c->points[0]->y+c->points[i-1]->y)/2);
5178 		sp->ttfindex = -1;
5179 		sp->prevcp.x = c->points[i-1]->x;
5180 		sp->prevcp.y = c->points[i-1]->y;
5181 		sp->prevcpselected = c->points[i-1]->selected;
5182 		sp->noprevcp = false;
5183 		if ( ss->last==NULL ) {
5184 		    ss->first = sp;
5185 		    ss->start_offset = 0;
5186 		} else
5187 		    SplineMake2(ss->last,sp);
5188 		ss->last = sp;
5189 	    }
5190 	    ss->last->nextcp.x = c->points[0]->x;
5191 	    ss->last->nextcp.y = c->points[0]->y;
5192 	    ss->last->nextcpselected = c->points[0]->selected;
5193 	    ss->last->nonextcp = false;
5194 	    ss->last->nextcpindex = start;
5195 	}
5196     } else {
5197 	for ( i=0; i<c->pt_cnt; ++i ) {
5198 	    if ( c->points[i]->on_curve )
5199 	break;
5200 	    ++next;
5201 	}
5202 	for ( i=0; i<c->pt_cnt; ++i ) {
5203 	    if ( !c->points[i]->on_curve )
5204 	continue;
5205 	    sp = SplinePointCreate(c->points[i]->x,c->points[i]->y);
5206 	    sp->pointtype = PyFF_ConvertToPointType(c->points[i]->type);
5207 	    sp->selected = c->points[i]->selected;
5208 	    sp->name = copy(c->points[i]->name);
5209 	    sp->ttfindex = next++;
5210 	    nexti = previ = -1;
5211 	    if ( i==0 )
5212 		previ = c->pt_cnt-1;
5213 	    else
5214 		previ = i-1;
5215 	    if ( !c->points[previ]->on_curve ) {
5216 		sp->prevcp.x = c->points[previ]->x;
5217 		sp->prevcp.y = c->points[previ]->y;
5218 		sp->prevcpselected = c->points[previ]->selected;
5219 		// if ( sp->prevcp.x!=sp->me.x || sp->prevcp.y!=sp->me.y ) // This is unnecessary since the other converter only makes a control point if there is supposed to be one.
5220 		    sp->noprevcp = false;
5221 	    }
5222 	    if ( i==c->pt_cnt-1 )
5223 		nexti = 0;
5224 	    else
5225 		nexti = i+1;
5226 	    if ( !c->points[nexti]->on_curve ) {
5227 		sp->nextcp.x = c->points[nexti]->x;
5228 		sp->nextcp.y = c->points[nexti]->y;
5229 		sp->nextcpselected = c->points[nexti]->selected;
5230 		next += 2;
5231 		// if ( sp->nextcp.x!=sp->me.x || sp->nextcp.y!=sp->me.y ) // This is unnecessary since the other converter only makes a control point if there is supposed to be one.
5232 		    sp->nonextcp = false;
5233 		if ( nexti==c->pt_cnt-1 )
5234 		    nexti = 0;
5235 		else
5236 		    ++nexti;
5237 		if ( c->points[nexti]->on_curve ) {
5238 		    SplinePointListsFree(ss);
5239                     free(sp);
5240 		    PyErr_Format(PyExc_TypeError, "In cubic splines there must be exactly 2 control points between on curve points");
5241 return( NULL );
5242 		}
5243 		if ( nexti==c->pt_cnt-1 )
5244 		    nexti = 0;
5245 		else
5246 		    ++nexti;
5247 		if ( !c->points[nexti]->on_curve ) {
5248 		    SplinePointListsFree(ss);
5249                     free(sp);
5250 		    PyErr_Format(PyExc_TypeError, "In cubic splines there must be exactly 2 control points between on curve points");
5251 return( NULL );
5252 		}
5253 	    }
5254 	    if ( ss->last==NULL ) {
5255 		ss->first = sp;
5256 		ss->start_offset = 0;
5257 	    } else
5258 		SplineMake3(ss->last,sp);
5259 	    ss->last = sp;
5260 	}
5261 	if ( ss->last==NULL ) {
5262 	    SplinePointListsFree(ss);
5263 	    PyErr_Format(PyExc_TypeError, "Contour has points but none are on-curve");
5264 return( NULL );
5265 	}
5266     }
5267     if ( c->closed ) {
5268 
5269 	SplineMake(ss->last,ss->first,c->is_quadratic);
5270 	if ( c->is_quadratic && (ss->last->nextcpselected || ss->first->prevcpselected) ) {
5271             // The convention for tracking selection of quadratic control
5272 	    // points is to use nextcpselected except at the tail of the
5273 	    // list, where it's prevcpselected on the first point.
5274 	     ss->last->nextcpselected = false;
5275 	     ss->first->prevcpselected = true;
5276 	}
5277 	ss->last = ss->first;
5278     }
5279     if ( tt_start!=NULL )
5280 	*tt_start = next;
5281     ok = _SPLCategorizePoints(ss, flags);
5282     if ( !ok ) {
5283 	SplinePointListsFree(ss);
5284 	// assert ( flags&pconvert_flag_check );
5285 	PyErr_Format(PyExc_TypeError, "At least one point has a geometry incompatible with its type");
5286 	return( NULL );
5287     }
5288     return( ss );
5289 }
5290 
SSFromContour(PyFF_Contour * c,int * tt_start)5291 SplineSet *SSFromContour(PyFF_Contour *c,int *tt_start) {
5292 	return _SSFromContour(c, tt_start, pconvert_flag_none);
5293 }
5294 
ContourFromSS(SplineSet * ss,PyFF_Contour * ret)5295 static PyFF_Contour *ContourFromSS(SplineSet *ss,PyFF_Contour *ret) {
5296     int k, cnt;
5297     SplinePoint *sp, *skip;
5298 
5299     if ( ret==NULL )
5300 	ret = (PyFF_Contour *) PyFFContour_new(&PyFF_ContourType,NULL,NULL);
5301     else
5302 	PyFFContour_clear(ret);
5303     if ( ss->spiro_cnt!=0 ) {
5304 	ret->spiro_cnt = ss->spiro_cnt;
5305 	ret->spiros = SpiroCPCopy(ss->spiros,NULL);
5306     }
5307     ret->name = copy(ss->contour_name);
5308     ret->closed = ss->first->prev!=NULL;
5309     for ( k=0; k<2; ++k ) {
5310 	if ( ss->first->next == NULL ) {
5311 	    if ( k )
5312 		ret->points[0] = PyFFPoint_CNew(ss->first->me.x,ss->first->me.y,true,
5313 		                                ss->first->selected,PyFF_ConvertFromPointType(ss->first->pointtype),
5314 						ss->first->name);
5315 	    cnt = 1;
5316 	} else if ( ss->first->next->order2 ) {
5317 	    ret->is_quadratic = true;
5318 	    cnt = 0;
5319 	    for ( sp=ss->first; ; ) {
5320 		if ( k ) {
5321 		    ret->points[cnt] = PyFFPoint_CNew(sp->me.x,sp->me.y,true,sp->selected,
5322 		                                      PyFF_ConvertFromPointType(sp->pointtype), sp->name);
5323 		    ret->points[cnt]->interpolated = SPInterpolate(sp);
5324 		}
5325 		++cnt;
5326 		if ( !sp->nonextcp ) {
5327 		    if ( k )
5328 			ret->points[cnt] = PyFFPoint_CNew(sp->nextcp.x,sp->nextcp.y,false,
5329 			                                  sp->nextcpselected,0,sp->name);
5330 		    ++cnt;
5331 		}
5332 		if ( sp->next==NULL )
5333 	    break;
5334 		sp = sp->next->to;
5335 		if ( sp==ss->first )
5336 	    break;
5337 	    }
5338 	    if ( k && ss->first->prevcpselected && ! ret->points[cnt-1]->on_curve )
5339 		ret->points[cnt-1]->selected = true;
5340 	} else {
5341 	    ret->is_quadratic = false;
5342 	    for ( sp=ss->first, cnt=0; ; ) {
5343 		if ( k )
5344 		    ret->points[cnt] = PyFFPoint_CNew(sp->me.x,sp->me.y,true, sp->selected,
5345 		                                      PyFF_ConvertFromPointType(sp->pointtype), sp->name);
5346 		++cnt;			/* Sp itself */
5347 		if ( sp->next==NULL )
5348 	    break;
5349 		if ( !sp->nonextcp || !sp->next->to->noprevcp ) {
5350 		    if ( k ) {
5351 			ret->points[cnt  ] = PyFFPoint_CNew(sp->nextcp.x,sp->nextcp.y,false,sp->nextcpselected,0,NULL);
5352 			ret->points[cnt+1] = PyFFPoint_CNew(sp->next->to->prevcp.x,sp->next->to->prevcp.y,false,sp->next->to->prevcpselected,0,NULL);
5353 		    }
5354 		    cnt += 2;		/* not a line => 2 control points */
5355 		}
5356 		sp = sp->next->to;
5357 		if ( sp==ss->first )
5358 	    break;
5359 	    }
5360 	}
5361 	if ( !k ) {
5362 	    if ( cnt>=ret->pt_max ) {
5363 		ret->pt_max = cnt;
5364 		PyMem_Resize(ret->points,PyFF_Point *,cnt);  /* Messes with ret->points */
5365 	    }
5366 	    ret->pt_cnt = cnt;
5367 	}
5368     }
5369 return( ret );
5370 }
5371 
_SSFromLayer(PyFF_Layer * layer,int flags)5372 static SplineSet *_SSFromLayer(PyFF_Layer *layer, int flags) {
5373     int start = 0;
5374     SplineSet *head=NULL, *tail, *cur;
5375     int i;
5376 
5377     for ( i=0; i<layer->cntr_cnt; ++i ) {
5378 	cur = _SSFromContour( layer->contours[i], &start, flags );
5379 	if ( cur!=NULL ) {
5380 	    if ( head==NULL )
5381 		head = cur;
5382 	    else
5383 		tail->next = cur;
5384 	    tail = cur;
5385 	} else if ( PyErr_Occurred() != NULL ) {
5386 	    SplinePointListsFree(head);
5387 	    return( NULL );
5388 	}
5389 	// Ignore empty contours
5390     }
5391     return( head );
5392 }
5393 
SSFromLayer(PyFF_Layer * layer)5394 static SplineSet *SSFromLayer(PyFF_Layer *layer) {
5395 	return _SSFromLayer(layer, pconvert_flag_none);
5396 }
5397 
LayerFromSS(SplineSet * ss,PyFF_Layer * layer)5398 static PyFF_Layer *LayerFromSS(SplineSet *ss,PyFF_Layer *layer) {
5399     SplineSet *cur;
5400     int cnt, i;
5401 
5402     if ( layer==NULL )
5403 	layer = (PyFF_Layer *) PyFFLayer_new(&PyFF_LayerType,NULL,NULL);
5404 
5405     for ( cnt=0, cur=ss; cur!=NULL; cur=cur->next, ++cnt );
5406     if ( cnt>layer->cntr_max ) {
5407          /* Messes with layer->contours */
5408         PyMem_Resize(layer->contours,PyFF_Contour *,layer->cntr_max = cnt );
5409     }
5410     if ( cnt>layer->cntr_cnt ) {
5411 	for ( i=layer->cntr_cnt; i<cnt; ++i )
5412 	    layer->contours[i] = NULL;
5413     } else if ( cnt<layer->cntr_cnt ) {
5414 	for ( i = cnt; i<layer->cntr_cnt; ++i )
5415 	    Py_DECREF( (PyObject *) layer->contours[i] );
5416     }
5417     layer->cntr_cnt = cnt;
5418     for ( cnt=0, cur=ss; cur!=NULL; cur=cur->next, ++cnt ) {
5419 	layer->contours[cnt] = ContourFromSS(cur,layer->contours[cnt]);
5420 	layer->is_quadratic = layer->contours[cnt]->is_quadratic;
5421     }
5422 return( layer );
5423 }
5424 
LayerFromLayer(Layer * inlayer,PyFF_Layer * ret)5425 static PyFF_Layer *LayerFromLayer(Layer *inlayer,PyFF_Layer *ret) {
5426     /* May want to copy fills and pens someday!! */
5427     PyFF_Layer *layer = LayerFromSS(inlayer->splines,ret);
5428     layer->is_quadratic = inlayer->order2;
5429 return( layer );
5430 }
5431 
5432 /* ************************************************************************** */
5433 /* GlyphPen Standard Methods */
5434 /* ************************************************************************** */
5435 
PyFF_GlyphPen_dealloc(PyFF_GlyphPen * self)5436 static void PyFF_GlyphPen_dealloc(PyFF_GlyphPen *self) {
5437     if ( self->sc!=NULL ) {
5438 	if ( self->changed )
5439 	    SCCharChangedUpdate(self->sc,self->layer);
5440 	self->sc = NULL;
5441     }
5442     Py_TYPE(self)->tp_free((PyObject *) self);
5443 }
5444 
PyFFGlyphPen_Str(PyFF_GlyphPen * self)5445 static PyObject *PyFFGlyphPen_Str(PyFF_GlyphPen *self) {
5446 return( PyUnicode_FromFormat( "<GlyphPen for %s>", self->sc->name ));
5447 }
5448 
5449 /* ************************************************************************** */
5450 /*  GlyphPen Methods  */
5451 /* ************************************************************************** */
GlyphClear(PyObject * self)5452 static void GlyphClear(PyObject *self) {
5453     SplineChar *sc = ((PyFF_GlyphPen *) self)->sc;
5454     int layer = ((PyFF_GlyphPen *) self)->layer;
5455     SCClearContents(sc,layer);
5456     ((PyFF_GlyphPen *) self)->replace = false;
5457 }
5458 
PyFFGlyphPen_moveTo(PyObject * self,PyObject * args)5459 static PyObject *PyFFGlyphPen_moveTo(PyObject *self, PyObject *args) {
5460     SplineChar *sc = ((PyFF_GlyphPen *) self)->sc;
5461     int layer = ((PyFF_GlyphPen *) self)->layer;
5462     SplineSet *ss;
5463     double x,y;
5464 
5465     if ( !((PyFF_GlyphPen *) self)->ended ) {
5466 	PyErr_Format(PyExc_EnvironmentError, "The moveTo operator may not be called while drawing a contour");
5467 return( NULL );
5468     }
5469     if ( !PyArg_ParseTuple( args, "(dd)", &x, &y )) {
5470 	PyErr_Clear();
5471 	if ( !PyArg_ParseTuple( args, "dd", &x, &y ))
5472 return( NULL );
5473     }
5474     if ( ((PyFF_GlyphPen *) self)->replace )
5475 	GlyphClear(self);
5476     ss = chunkalloc(sizeof(SplineSet));
5477     ss->next = sc->layers[layer].splines;
5478     sc->layers[layer].splines = ss;
5479     ss->first = ss->last = SplinePointCreate(x,y);
5480     ss->start_offset = 0;
5481 
5482     ((PyFF_GlyphPen *) self)->ended = false;
5483     ((PyFF_GlyphPen *) self)->changed = true;
5484 Py_RETURN( self );
5485 }
5486 
PyFFGlyphPen_lineTo(PyObject * self,PyObject * args)5487 static PyObject *PyFFGlyphPen_lineTo(PyObject *self, PyObject *args) {
5488     SplineChar *sc = ((PyFF_GlyphPen *) self)->sc;
5489     int layer = ((PyFF_GlyphPen *) self)->layer;
5490     SplinePoint *sp;
5491     SplineSet *ss;
5492     double x,y;
5493 
5494     if ( ((PyFF_GlyphPen *) self)->ended ) {
5495 	PyErr_Format(PyExc_EnvironmentError, "The lineTo operator must be preceded by a moveTo operator" );
5496 return( NULL );
5497     }
5498     if ( !PyArg_ParseTuple( args, "(dd)", &x, &y )) {
5499 	PyErr_Clear();
5500 	if ( !PyArg_ParseTuple( args, "dd", &x, &y ))
5501 return( NULL );
5502     }
5503     ss = sc->layers[layer].splines;
5504     sp = SplinePointCreate(x,y);
5505     SplineMake(ss->last,sp,sc->layers[layer].order2);
5506     ss->last = sp;
5507 
5508 Py_RETURN( self );
5509 }
5510 
PyFFGlyphPen_curveTo(PyObject * self,PyObject * args)5511 static PyObject *PyFFGlyphPen_curveTo(PyObject *self, PyObject *args) {
5512     SplineChar *sc = ((PyFF_GlyphPen *) self)->sc;
5513     int layer = ((PyFF_GlyphPen *) self)->layer;
5514     SplinePoint *sp;
5515     SplineSet *ss;
5516     double x[3],y[3];
5517 
5518     if ( ((PyFF_GlyphPen *) self)->ended ) {
5519 	PyErr_Format(PyExc_EnvironmentError, "The curveTo operator must be preceded by a moveTo operator" );
5520 return( NULL );
5521     }
5522     if ( sc->layers[layer].order2 ) {
5523 	if ( !PyArg_ParseTuple( args, "(dd)(dd)", &x[1], &y[1], &x[2], &y[2] )) {
5524 	    PyErr_Clear();
5525 	    if ( !PyArg_ParseTuple( args, "dddd", &x[1], &y[1], &x[2], &y[2] ))
5526 return( NULL );
5527 	}
5528 	x[0] = x[1]; y[0] = y[1];
5529     } else {
5530 	if ( !PyArg_ParseTuple( args, "(dd)(dd)(dd)", &x[0], &y[0], &x[1], &y[1], &x[2], &y[2] )) {
5531 	    PyErr_Clear();
5532 	    if ( !PyArg_ParseTuple( args, "dddddd", &x[0], &y[0], &x[1], &y[1], &x[2], &y[2] ))
5533 return( NULL );
5534 	}
5535     }
5536     ss = sc->layers[layer].splines;
5537     sp = SplinePointCreate(x[2],y[2]);
5538     sp->prevcp.x = x[1]; sp->prevcp.y = y[1];
5539     sp->noprevcp = false;
5540     ss->last->nextcp.x = x[0], ss->last->nextcp.y = y[0];
5541     ss->last->nonextcp = false;
5542     SplineMake(ss->last,sp,sc->layers[layer].order2);
5543     ss->last = sp;
5544 
5545 Py_RETURN( self );
5546 }
5547 
PyFFGlyphPen_qCurveTo(PyObject * self,PyObject * args)5548 static PyObject *PyFFGlyphPen_qCurveTo(PyObject *self, PyObject *args) {
5549     SplineChar *sc = ((PyFF_GlyphPen *) self)->sc;
5550     int layer = ((PyFF_GlyphPen *) self)->layer;
5551     SplinePoint *sp;
5552     SplineSet *ss;
5553     double x0,y0, x1,y1, x2,y2;
5554     int len, i;
5555     PyObject *pt_tuple;
5556 
5557     if ( !sc->layers[layer].order2 ) {
5558 	PyErr_Format(PyExc_EnvironmentError, "qCurveTo only applies to quadratic fonts" );
5559 return( NULL );
5560     }
5561 
5562     len = PySequence_Size(args);
5563     if ( len < 2 ) {
5564 	PyErr_Format(PyExc_EnvironmentError, "qCurveTo requires at least two arguments");
5565 return( NULL );
5566     }
5567     else if ( PySequence_GetItem(args,len-1)== Py_None ) {
5568 	--len;
5569 	if ( !((PyFF_GlyphPen *) self)->ended ) {
5570 	    PyErr_Format(PyExc_EnvironmentError, "qCurveTo must describe an entire contour if its last argument is None");
5571 return( NULL );
5572 	} else if ( len<2 ) {
5573 	    PyErr_Format(PyExc_EnvironmentError, "qCurveTo must have at least two tuples");
5574 return( NULL );
5575 	}
5576 
5577 	pt_tuple = PySequence_GetItem(args,0);
5578 	if ( !PyArg_ParseTuple(pt_tuple,"dd", &x0, &y0 ))
5579 return( NULL );
5580 
5581 	ss = chunkalloc(sizeof(SplineSet));
5582 	ss->next = sc->layers[layer].splines;
5583 	sc->layers[layer].splines = ss;
5584 
5585 	x1 = x0; y1 = y0;
5586 	for ( i=1; i<len; ++i ) {
5587 	    pt_tuple = PySequence_GetItem(args,i);
5588 	    if ( !PyArg_ParseTuple(pt_tuple,"dd", &x2, &y2 ))
5589 return( NULL );
5590 	    sp = SplinePointCreate((x1+x2)/2,(y1+y2)/2);
5591 	    sp->noprevcp = false;
5592 	    sp->prevcp.x = x1; sp->prevcp.y = y1;
5593 	    sp->nonextcp = false;
5594 	    sp->nextcp.x = x2; sp->nextcp.y = y2;
5595 	    if ( ss->first==NULL ) {
5596 		ss->first = ss->last = sp;
5597 		ss->start_offset = 0;
5598 	    } else {
5599 		SplineMake2(ss->last,sp);
5600 		ss->last = sp;
5601 	    }
5602 	    x1=x2; y1=y2;
5603 	}
5604 	sp = SplinePointCreate((x0+x2)/2,(y0+y2)/2);
5605 	sp->noprevcp = false;
5606 	sp->prevcp.x = x2; sp->prevcp.y = y2;
5607 	sp->nonextcp = false;
5608 	sp->nextcp.x = x0; sp->nextcp.y = y0;
5609 	SplineMake2(ss->last,sp);
5610 	SplineMake2(sp,ss->first);
5611 	ss->last = ss->first;
5612 
5613 	/*((PyFF_GlyphPen *) self)->ended = true;*/
5614 	((PyFF_GlyphPen *) self)->changed = true;
5615     } else {
5616 	if ( ((PyFF_GlyphPen *) self)->ended ) {
5617 	    PyErr_Format(PyExc_EnvironmentError, "The qCurveTo operator must be preceded by a moveTo operator" );
5618 return( NULL );
5619 	} else if ( len<2 ) {
5620 	    PyErr_Format(PyExc_EnvironmentError, "qCurveTo must have at least two tuples");
5621 return( NULL );
5622 	}
5623 	ss = sc->layers[layer].splines;
5624 	pt_tuple = PySequence_GetItem(args,0);
5625 	if ( !PyArg_ParseTuple(pt_tuple,"dd", &x1, &y1 ))
5626 return( NULL );
5627 	ss->last->nextcp.x = x1; ss->last->nextcp.y = y1;
5628 	ss->last->nonextcp = false;
5629 	for ( i=1; i<len-1; ++i ) {
5630 	    pt_tuple = PySequence_GetItem(args,i);
5631 	    if ( !PyArg_ParseTuple(pt_tuple,"dd", &x2, &y2 ))
5632 return( NULL );
5633 	    sp = SplinePointCreate((x1+x2)/2,(y1+y2)/2);
5634 	    sp->noprevcp = false;
5635 	    sp->prevcp.x = x1; sp->prevcp.y = y1;
5636 	    sp->nonextcp = false;
5637 	    sp->nextcp.x = x2; sp->nextcp.y = y2;
5638 	    SplineMake2(ss->last,sp);
5639 	    ss->last = sp;
5640 	    x1=x2; y1=y2;
5641 	}
5642 	pt_tuple = PySequence_GetItem(args,i);
5643 	if ( !PyArg_ParseTuple(pt_tuple,"dd", &x2, &y2 ))
5644 return( NULL );
5645 	sp = SplinePointCreate(x2,y2);
5646 	sp->noprevcp = false;
5647 	sp->prevcp.x = x1; sp->prevcp.y = y1;
5648 	SplineMake2(ss->last,sp);
5649 	ss->last = sp;
5650     }
5651 
5652 Py_RETURN( self );
5653 }
5654 
PyFFGlyphPen_closePath(PyObject * self,PyObject * UNUSED (args))5655 static PyObject *PyFFGlyphPen_closePath(PyObject *self, PyObject *UNUSED(args)) {
5656     SplineChar *sc = ((PyFF_GlyphPen *) self)->sc;
5657     int layer = ((PyFF_GlyphPen *) self)->layer;
5658     SplineSet *ss;
5659 
5660     if ( ((PyFF_GlyphPen *) self)->ended ) {
5661 	PyErr_Format(PyExc_EnvironmentError, "The closePath operator requires and open path to close" );
5662 return( NULL );
5663     }
5664 
5665     ss = sc->layers[layer].splines;
5666     if ( ss->first!=ss->last && RealNear(ss->first->me.x,ss->last->me.x) &&
5667 	    RealNear(ss->first->me.y,ss->last->me.y)) {
5668 	ss->first->prevcp = ss->last->prevcp;
5669 	ss->first->noprevcp = ss->last->noprevcp;
5670 	ss->last->prev->to = ss->first;
5671 	ss->first->prev = ss->last->prev;
5672 	SplinePointFree(ss->last);
5673     } else {
5674 	SplineMake(ss->last,ss->first,sc->layers[layer].order2);
5675     }
5676     ss->last = ss->first;
5677 
5678     ((PyFF_GlyphPen *) self)->ended = true;
5679 Py_RETURN( self );
5680 }
5681 
PyFFGlyphPen_endPath(PyObject * self,PyObject * UNUSED (args))5682 static PyObject *PyFFGlyphPen_endPath(PyObject *self, PyObject *UNUSED(args)) {
5683 
5684     if ( ((PyFF_GlyphPen *) self)->ended ) {
5685 	PyErr_Format(PyExc_EnvironmentError, "The endPath operator must be preceded path operations" );
5686 return( NULL );
5687     }
5688 
5689     ((PyFF_GlyphPen *) self)->ended = true;
5690 Py_RETURN( self );
5691 }
5692 
PyFFGlyphPen_addComponent(PyObject * self,PyObject * args)5693 static PyObject *PyFFGlyphPen_addComponent(PyObject *self, PyObject *args) {
5694     SplineChar *sc = ((PyFF_GlyphPen *) self)->sc;
5695     int layer = ((PyFF_GlyphPen *) self)->layer;
5696     real transform[6];
5697     SplineChar *rsc;
5698     double m[6];
5699     char *str;
5700     int j;
5701 
5702     if ( !((PyFF_GlyphPen *) self)->ended ) {
5703 	PyErr_Format(PyExc_EnvironmentError, "The addComponent operator may not be called while drawing a contour");
5704 return( NULL );
5705     }
5706     if ( ((PyFF_GlyphPen *) self)->replace )
5707 	GlyphClear(self);
5708 
5709     memset(m,0,sizeof(m));
5710     m[0] = m[3] = 1;
5711     if ( !PyArg_ParseTuple(args,"s|(dddddd)",&str,
5712 	    &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) )
5713 return( NULL );
5714     rsc = SFGetChar(sc->parent,-1,str);
5715     if ( rsc==NULL ) {
5716 	PyErr_Format(PyExc_EnvironmentError, "No glyph named %s", str);
5717 return( NULL );
5718     }
5719     for ( j=0; j<6; ++j )
5720 	transform[j] = m[j];
5721     _SCAddRef(sc,rsc,layer,transform);
5722 
5723 Py_RETURN( self );
5724 }
5725 
5726 static PyMethodDef PyFF_GlyphPen_methods[] = {
5727     { "moveTo", PyFFGlyphPen_moveTo, METH_VARARGS, "Start a new contour at a point (a two element tuple)" },
5728     { "lineTo", PyFFGlyphPen_lineTo, METH_VARARGS, "Draws a line from the current point to the argument (a two element tuple)" },
5729     { "curveTo", PyFFGlyphPen_curveTo, METH_VARARGS, "Draws a cubic or quadratic bezier curve from the current point to the last arg" },
5730     { "qCurveTo", PyFFGlyphPen_qCurveTo, METH_VARARGS, "Draws a series of quadratic bezier curves" },
5731     { "closePath", PyFFGlyphPen_closePath, METH_VARARGS, "Closes the current contour (and ends it)" },
5732     { "endPath", PyFFGlyphPen_endPath, METH_VARARGS, "Ends the current contour (without closing it)" },
5733     { "addComponent", PyFFGlyphPen_addComponent, METH_VARARGS, "Adds a reference into the glyph" },
5734     PYMETHODDEF_EMPTY /* Sentinel */
5735 };
5736 /* ************************************************************************** */
5737 /*  GlyphPen Type  */
5738 /* ************************************************************************** */
5739 
5740 static PyTypeObject PyFF_GlyphPenType = {
5741     PyVarObject_HEAD_INIT(NULL, 0)
5742     "fontforge.glyphPen",      /* tp_name */
5743     sizeof(PyFF_GlyphPen),     /* tp_basicsize */
5744     0,                         /* tp_itemsize */
5745     (destructor) PyFF_GlyphPen_dealloc, /* tp_dealloc */
5746     0,                         /* tp_vectorcall_offset */
5747     NULL,                      /* tp_getattr */
5748     NULL,                      /* tp_setattr */
5749     NULL,                      /* tp_compare */
5750     NULL,                      /* tp_repr */
5751     NULL,                      /* tp_as_number */
5752     NULL,                      /* tp_as_sequence */
5753     NULL,                      /* tp_as_mapping */
5754     NULL,                      /* tp_hash */
5755     NULL,                      /* tp_call */
5756     (reprfunc) PyFFGlyphPen_Str,/* tp_str */
5757     NULL,                      /* tp_getattro */
5758     NULL,                      /* tp_setattro */
5759     NULL,                      /* tp_as_buffer */
5760     Py_TPFLAGS_DEFAULT,        /* tp_flags */
5761     "FontForge GlyphPen object",/* tp_doc */
5762     NULL,                      /* tp_traverse */
5763     NULL,                      /* tp_clear */
5764     NULL,                      /* tp_richcompare */
5765     0,                         /* tp_weaklistoffset */
5766     NULL,                      /* tp_iter */
5767     NULL,                      /* tp_iternext */
5768     PyFF_GlyphPen_methods,     /* tp_methods */
5769     NULL,                      /* tp_members */
5770     NULL,                      /* tp_getset */
5771     NULL,                      /* tp_base */
5772     NULL,                      /* tp_dict */
5773     NULL,                      /* tp_descr_get */
5774     NULL,                      /* tp_descr_set */
5775     0,                         /* tp_dictoffset */
5776     NULL,/*(initproc)PyFF_GlyphPen_init*/ /* tp_init */
5777     NULL,                      /* tp_alloc */
5778     NULL,/*PyFF_GlyphPen_new*/ /* tp_new */
5779     NULL,                      /* tp_free */
5780     NULL,                      /* tp_is_gc */
5781     NULL,                      /* tp_bases */
5782     NULL,                      /* tp_mro */
5783     NULL,                      /* tp_cache */
5784     NULL,                      /* tp_subclasses */
5785     NULL,                      /* tp_weaklist */
5786     NULL,                      /* tp_del */
5787     0,                         /* tp_version_tag */
5788 };
5789 
5790 /* ************************************************************************** */
5791 /* Glyph Utilities */
5792 /* ************************************************************************** */
5793 
PyFF_Glyph_get_layer_references(PyFF_Glyph * self,void * UNUSED (closure),int layer)5794 static PyObject *PyFF_Glyph_get_layer_references(PyFF_Glyph *self, void *UNUSED(closure),
5795 	int layer) {
5796     RefChar *ref;
5797     int cnt;
5798     SplineChar *sc = self->sc;
5799     PyObject *tuple;
5800 
5801     for ( ref=sc->layers[layer].refs, cnt=0; ref!=NULL; ++cnt, ref=ref->next );
5802     tuple = PyTuple_New(cnt);
5803     for ( ref=sc->layers[layer].refs, cnt=0; ref!=NULL; ++cnt, ref=ref->next )
5804 	PyTuple_SET_ITEM(tuple,cnt,Py_BuildValue("(s(dddddd))", ref->sc->name,
5805 		ref->transform[0], ref->transform[1], ref->transform[2],
5806 		ref->transform[3], ref->transform[4], ref->transform[5]));
5807 return( tuple );
5808 }
5809 
PyFF_Glyph_set_layer_references(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure),int layer)5810 static int PyFF_Glyph_set_layer_references(PyFF_Glyph *self,PyObject *value,
5811 	void *UNUSED(closure), int layer) {
5812     int i, j, cnt;
5813     double m[6];
5814     real transform[6];
5815     char *str;
5816     SplineChar *sc = self->sc, *rsc;
5817     SplineFont *sf = sc->parent;
5818     RefChar *refs, *next;
5819 
5820     if ( !PySequence_Check(value)) {
5821 	PyErr_Format(PyExc_TypeError, "Value must be a tuple of references");
5822 return( -1 );
5823     }
5824     cnt = PySequence_Size(value);
5825     for ( refs=sc->layers[layer].refs; refs!=NULL; refs = next ) {
5826 	next = refs->next;
5827 	SCRemoveDependent(sc,refs,layer);
5828     }
5829     sc->layers[layer].refs = NULL;
5830     for ( i=0; i<cnt; ++i ) {
5831 	if ( !PyArg_ParseTuple(PySequence_GetItem(value,i),"s(dddddd)",&str,
5832 		&m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) )
5833 return( -1 );
5834 	rsc = SFGetChar(sf,-1,str);
5835 	if ( rsc==NULL ) {
5836 	    PyErr_Format(PyExc_EnvironmentError, "No glyph named %s", str);
5837 return( -1 );
5838 	}
5839 	for ( j=0; j<6; ++j )
5840 	    transform[j] = m[j];
5841 	_SCAddRef(sc,rsc,layer,transform);
5842     }
5843 return( 0 );
5844 }
5845 
PyFF_Glyph_get_a_layer(PyFF_Glyph * self,void * vli)5846 static PyObject *PyFF_Glyph_get_a_layer(PyFF_Glyph *self,void *vli) {
5847     SplineChar *sc = self->sc;
5848     Layer *layer;
5849     PyFF_Layer *ly;
5850     int layeri = (int)(size_t)vli;
5851 
5852     if ( layeri<ly_grid || layeri>=sc->layer_cnt ) {
5853 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
5854 return( NULL );
5855     } else if ( layeri==ly_grid )
5856 	layer = &sc->parent->grid;
5857     else
5858 	layer = &sc->layers[layeri];
5859     ly = LayerFromLayer(layer,NULL);
5860 return( (PyObject * ) ly );
5861 }
5862 
PyFF_Glyph_CSetLayer(PyFF_Glyph * self,PyObject * value,int layeri,int flags)5863 static int PyFF_Glyph_CSetLayer(PyFF_Glyph *self, PyObject *value, int layeri, int flags) {
5864     SplineChar *sc = self->sc;
5865     Layer *layer;
5866     SplineSet *ss = NULL, *newss;
5867     int isquad;
5868 
5869     if ( layeri<ly_grid || layeri>=sc->layer_cnt ) {
5870 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
5871 	return( -1 );
5872     } else if ( layeri==ly_grid )
5873 	layer = &sc->parent->grid;
5874     else
5875 	layer = &sc->layers[layeri];
5876     if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(value)) ) {
5877 	isquad = ((PyFF_Layer *) value)->is_quadratic;
5878 	ss = _SSFromLayer( (PyFF_Layer *) value, flags);
5879     } else if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(value)) ) {
5880 	isquad = ((PyFF_Contour *) value)->is_quadratic;
5881 	ss = _SSFromContour( (PyFF_Contour *) value, NULL, flags);
5882     } else {
5883 	PyErr_Format(PyExc_TypeError, "Argument must be a layer or a contour" );
5884 	return( -1 );
5885     }
5886 
5887     if ( PyErr_Occurred() != NULL ) {
5888 	return( -1 );
5889     }
5890 
5891     if ( layer->order2!=isquad ) {
5892 	if ( layer->order2 )
5893 	    newss = SplineSetsTTFApprox(ss);
5894 	else
5895 	    newss = SplineSetsPSApprox(ss);
5896 	SplinePointListsFree(ss);
5897 	ss = newss;
5898     }
5899     SplinePointListsFree(layer->splines);
5900     layer->splines = ss;
5901 
5902     SCCharChangedUpdate(sc,self->layer);
5903 return( 0 );
5904 }
5905 
PyFF_Glyph_set_a_layer(PyFF_Glyph * self,PyObject * value,void * vli)5906 static int PyFF_Glyph_set_a_layer(PyFF_Glyph *self,PyObject *value, void *vli) {
5907 	return PyFF_Glyph_CSetLayer(self, value, (int)(size_t)vli, pconvert_flag_all|pconvert_flag_by_geom);
5908 }
5909 
SFFindLayerIndexByName(SplineFont * sf,const char * name)5910 static int SFFindLayerIndexByName(SplineFont *sf,const char *name) {
5911     int l;
5912 
5913     if ( name!=NULL ) {
5914 	for ( l=0; l<sf->layer_cnt; ++l ) {
5915 	    if ( strcmp(sf->layers[l].name,name)==0 )
5916 return( l );
5917 	}
5918     }
5919     PyErr_Format(PyExc_ValueError, "Bad layer name: %s", name );
5920 return( -1 );
5921 }
5922 
5923 /* ************************************************************************** */
5924 /* Glyph helper types */
5925 /* ************************************************************************** */
5926 
5927 /* ************************************************************************** */
5928 /* Layers dictionary iterator type */
5929 /* ************************************************************************** */
5930 
5931 typedef struct {
5932 	PyObject_HEAD
5933 	PyFF_LayerArray *layers;
5934 	int pos;
5935 } layersiterobject;
5936 static PyTypeObject PyFF_LayerArrayIterType;
5937 
layersiter_new(PyObject * layers)5938 static PyObject *layersiter_new(PyObject *layers) {
5939     layersiterobject *di;
5940     di = PyObject_New(layersiterobject, &PyFF_LayerArrayIterType);
5941     if (di == NULL)
5942 return NULL;
5943     Py_INCREF(layers);
5944     di->layers = (PyFF_LayerArray *) layers;
5945     di->pos = 0;
5946 return (PyObject *)di;
5947 }
5948 
layersiter_dealloc(layersiterobject * di)5949 static void layersiter_dealloc(layersiterobject *di) {
5950     Py_XDECREF(di->layers);
5951     PyObject_Del(di);
5952 }
5953 
layersiter_iternextkey(layersiterobject * di)5954 static PyObject *layersiter_iternextkey(layersiterobject *di) {
5955     PyFF_LayerArray *d = di->layers;
5956     SplineFont *sf;
5957 
5958     if (d == NULL )
5959 return NULL;
5960     sf = d->sc->parent;
5961 
5962     if ( di->pos<sf->layer_cnt )
5963 return( Py_BuildValue("s",sf->layers[di->pos++].name) );
5964 
5965 return NULL;
5966 }
5967 
5968 static PyTypeObject PyFF_LayerArrayIterType = {
5969     PyVarObject_HEAD_INIT(NULL, 0)
5970     "fontforge.layer_array_iterator",  /* tp_name */
5971     sizeof(layersiterobject),  /* tp_basicsize */
5972     0,                         /* tp_itemsize */
5973     /* methods */
5974     (destructor)layersiter_dealloc, /* tp_dealloc */
5975     0,                         /* tp_vectorcall_offset */
5976     NULL,                      /* tp_getattr */
5977     NULL,                      /* tp_setattr */
5978     NULL,                      /* tp_compare */
5979     NULL,                      /* tp_repr */
5980     NULL,                      /* tp_as_number */
5981     NULL,                      /* tp_as_sequence */
5982     NULL,                      /* tp_as_mapping */
5983     NULL,                      /* tp_hash */
5984     NULL,                      /* tp_call */
5985     NULL,                      /* tp_str */
5986     NULL,                      /* tp_getattro */
5987     NULL,                      /* tp_setattro */
5988     NULL,                      /* tp_as_buffer */
5989     Py_TPFLAGS_DEFAULT,        /* tp_flags */
5990     NULL,                      /* tp_doc */
5991     NULL,                      /* tp_traverse */
5992     NULL,                      /* tp_clear */
5993     NULL,                      /* tp_richcompare */
5994     0,                         /* tp_weaklistoffset */
5995     PyObject_SelfIter,         /* tp_iter */
5996     (iternextfunc)layersiter_iternextkey, /* tp_iternext */
5997     NULL,                      /* tp_methods */
5998     NULL,                      /* tp_members */
5999     NULL,                      /* tp_getset */
6000     NULL,                      /* tp_base */
6001     NULL,                      /* tp_dict */
6002     NULL,                      /* tp_descr_get */
6003     NULL,                      /* tp_descr_set */
6004     0,                         /* tp_dictoffset */
6005     NULL,/*(initproc)PyFF_GlyphPen_init*/ /* tp_init */
6006     NULL,                      /* tp_alloc */
6007     NULL,/*PyFF_GlyphPen_new*/ /* tp_new */
6008     NULL,                      /* tp_free */
6009     NULL,                      /* tp_is_gc */
6010     NULL,                      /* tp_bases */
6011     NULL,                      /* tp_mro */
6012     NULL,                      /* tp_cache */
6013     NULL,                      /* tp_subclasses */
6014     NULL,                      /* tp_weaklist */
6015     NULL,                      /* tp_del */
6016     0,                         /* tp_version_tag */
6017 };
6018 
6019 /* ************************************************************************** */
6020 /* Layers Array Standard Methods */
6021 /* ************************************************************************** */
6022 
PyFF_LayerArray_dealloc(PyFF_LayerArray * self)6023 static void PyFF_LayerArray_dealloc(PyFF_LayerArray *self) {
6024     self->sc = NULL;
6025     Py_TYPE(self)->tp_free((PyObject *) self);
6026 }
6027 
PyFFLayerArray_Str(PyFF_LayerArray * self)6028 static PyObject *PyFFLayerArray_Str(PyFF_LayerArray *self) {
6029 return( PyUnicode_FromFormat( "<Layers Array for %s>", self->sc->name ));
6030 }
6031 
PyFF_LayerArray_get_font(PyFF_LayerArray * self,void * UNUSED (closure))6032 static PyObject *PyFF_LayerArray_get_font(PyFF_LayerArray *self, void *UNUSED(closure)) {
6033     PyFF_Font *font = PyFF_FontForSC( self->sc );
6034     if ( font==NULL )
6035 Py_RETURN_NONE;
6036     Py_INCREF(font);
6037     return (PyObject*)font;
6038 }
6039 
PyFF_LayerArray_get_glyph(PyFF_LayerArray * self,void * UNUSED (closure))6040 static PyObject *PyFF_LayerArray_get_glyph(PyFF_LayerArray *self, void *UNUSED(closure)) {
6041     PyFF_Glyph *glyph = PyFF_GlyphForSC( self->sc );
6042     if ( glyph==NULL )
6043 Py_RETURN_NONE;
6044     Py_INCREF(glyph);
6045     return (PyObject*)glyph;
6046 }
6047 
6048 /* ************************************************************************** */
6049 /* ****************************** Layers Array ****************************** */
6050 /* ************************************************************************** */
6051 
PyFF_LayerArrayLength(PyObject * self)6052 static Py_ssize_t PyFF_LayerArrayLength( PyObject *self ) {
6053     SplineChar *sc = ((PyFF_LayerArray *) self)->sc;
6054     if ( sc==NULL )
6055 return( 0 );
6056     else
6057 return( sc->layer_cnt );
6058 }
6059 
LayerFromLayerIndex(SplineChar * sc,PyObject * index)6060 static PyObject *LayerFromLayerIndex( SplineChar *sc, PyObject *index ) {
6061     int layer;
6062 
6063     if ( PyUnicode_Check(index)) {
6064 	const char *name = PyUnicode_AsUTF8(index);
6065 	if (name == NULL) {
6066 	    return NULL;
6067 	}
6068 	layer = SFFindLayerIndexByName(sc->parent,name);
6069 	if ( layer<0 )
6070 return( NULL );
6071     } else if ( PyLong_Check(index)) {
6072 	layer = PyLong_AsLong(index);
6073     } else {
6074 	PyErr_Format(PyExc_TypeError, "Index must be a layer name or index" );
6075 return( NULL );
6076     }
6077 return( PyFF_Glyph_get_a_layer((PyFF_Glyph *) PySC_From_SC(sc),(void *)(size_t)layer));
6078 }
6079 
PyFF_LayerArrayIndex(PyObject * self,PyObject * index)6080 static PyObject *PyFF_LayerArrayIndex( PyObject *self, PyObject *index ) {
6081     SplineChar *sc = ((PyFF_LayerArray *) self)->sc;
6082     return LayerFromLayerIndex(sc, index);
6083 }
6084 
PyFF_LayerArrayIndexAssign(PyObject * self,PyObject * index,PyObject * value)6085 static int PyFF_LayerArrayIndexAssign( PyObject *self, PyObject *index, PyObject *value ) {
6086     SplineChar *sc = ((PyFF_LayerArray *) self)->sc;
6087     int layer;
6088 
6089     if ( PyUnicode_Check(index)) {
6090 	const char *name = PyUnicode_AsUTF8(index);
6091 	if (name == NULL) {
6092 	    return -1;
6093 	}
6094 	layer = SFFindLayerIndexByName(sc->parent,name);
6095 	if ( layer<0 )
6096 return( -1 );
6097     } else if ( PyLong_Check(index)) {
6098 	layer = PyLong_AsLong(index);
6099     } else {
6100 	PyErr_Format(PyExc_TypeError, "Index must be a layer name or index" );
6101 return( -1 );
6102     }
6103 return( PyFF_Glyph_CSetLayer((PyFF_Glyph *) PySC_From_SC(sc),value,layer,pconvert_flag_all|pconvert_flag_by_geom));
6104 }
6105 
6106 static PyGetSetDef PyFF_LayerArray_getset[] = {
6107     {(char *)"font",
6108      (getter)PyFF_LayerArray_get_font, NULL,
6109      (char *)"returns the font to which this object belongs", NULL},
6110     {(char *)"glyph",
6111      (getter)PyFF_LayerArray_get_glyph, NULL,
6112      (char *)"returns the glyph to which this object belongs", NULL},
6113     PYGETSETDEF_EMPTY  /* Sentinel */
6114 };
6115 
6116 static PyMappingMethods PyFF_LayerArrayMapping = {
6117     PyFF_LayerArrayLength,		/* length */
6118     PyFF_LayerArrayIndex,		/* subscript */
6119     PyFF_LayerArrayIndexAssign	/* subscript assign */
6120 };
6121 
6122 /* ************************************************************************** */
6123 /* ************************* initializer routines *************************** */
6124 /* ************************************************************************** */
6125 
6126 static PyTypeObject PyFF_LayerArrayType = {
6127     PyVarObject_HEAD_INIT(NULL, 0)
6128     "fontforge.layer_array", /* tp_name */
6129     sizeof(PyFF_LayerArray),   /* tp_basicsize */
6130     0,                         /* tp_itemsize */
6131     (destructor) PyFF_LayerArray_dealloc, /* tp_dealloc */
6132     0,                         /* tp_vectorcall_offset */
6133     NULL,                      /* tp_getattr */
6134     NULL,                      /* tp_setattr */
6135     NULL,                      /* tp_compare */
6136     NULL,                      /* tp_repr */
6137     NULL,                      /* tp_as_number */
6138     NULL,                      /* tp_as_sequence */
6139     &PyFF_LayerArrayMapping,   /* tp_as_mapping */
6140     NULL,                      /* tp_hash */
6141     NULL,                      /* tp_call */
6142     (reprfunc) PyFFLayerArray_Str, /*tp_str */
6143     NULL,                      /* tp_getattro */
6144     NULL,                      /* tp_setattro */
6145     NULL,                      /* tp_as_buffer */
6146     Py_TPFLAGS_DEFAULT,        /* tp_flags */
6147     "FontForge layers array",  /* tp_doc */
6148     NULL,                      /* tp_traverse */
6149     NULL,                      /* tp_clear */
6150     NULL,                      /* tp_richcompare */
6151     0,                         /* tp_weaklistoffset */
6152     layersiter_new,            /* tp_iter */
6153     NULL,                      /* tp_iternext */
6154     NULL,                      /* tp_methods */
6155     NULL,                      /* tp_members */
6156     PyFF_LayerArray_getset,    /* tp_getset */
6157     NULL,                      /* tp_base */
6158     NULL,                      /* tp_dict */
6159     NULL,                      /* tp_descr_get */
6160     NULL,                      /* tp_descr_set */
6161     0,                         /* tp_dictoffset */
6162     NULL,                      /* tp_init */
6163     NULL,                      /* tp_alloc */
6164     NULL,                      /* tp_new */
6165     NULL,                      /* tp_free */
6166     NULL,                      /* tp_is_gc */
6167     NULL,                      /* tp_bases */
6168     NULL,                      /* tp_mro */
6169     NULL,                      /* tp_cache */
6170     NULL,                      /* tp_subclasses */
6171     NULL,                      /* tp_weaklist */
6172     NULL,                      /* tp_del */
6173     0,                         /* tp_version_tag */
6174 };
6175 
6176 /* ************************************************************************** */
6177 /* References Array Standard Methods */
6178 /* ************************************************************************** */
6179 
PyFF_RefArray_dealloc(PyFF_RefArray * self)6180 static void PyFF_RefArray_dealloc(PyFF_RefArray *self) {
6181     self->sc = NULL;
6182     Py_TYPE(self)->tp_free((PyObject *) self);
6183 }
6184 
PyFFReferences_Str(PyFF_RefArray * self)6185 static PyObject *PyFFReferences_Str(PyFF_RefArray *self) {
6186 return( PyUnicode_FromFormat( "<Layer References Array for %s>", self->sc->name ));
6187 }
6188 
6189 /* ************************************************************************** */
6190 /* ****************************** References Array ****************************** */
6191 /* ************************************************************************** */
6192 
PyFF_RefArrayLength(PyObject * self)6193 static Py_ssize_t PyFF_RefArrayLength( PyObject *self ) {
6194     SplineChar *sc = ((PyFF_RefArray *) self)->sc;
6195     if ( sc==NULL )
6196 return( 0 );
6197     else
6198 return( sc->layer_cnt );
6199 }
6200 
PyFF_RefArrayIndex(PyObject * self,PyObject * index)6201 static PyObject *PyFF_RefArrayIndex( PyObject *self, PyObject *index ) {
6202     SplineChar *sc = ((PyFF_RefArray *) self)->sc;
6203     int layer;
6204 
6205     if ( PyUnicode_Check(index)) {
6206 	const char *name = PyUnicode_AsUTF8(index);
6207 	if (name == NULL) {
6208 	    return NULL;
6209 	}
6210 	layer = SFFindLayerIndexByName(sc->parent,name);
6211 	if ( layer<0 )
6212 return( NULL );
6213     } else if ( PyLong_Check(index)) {
6214 	layer = PyLong_AsLong(index);
6215     } else {
6216 	PyErr_Format(PyExc_TypeError, "Index must be a layer name or index" );
6217 return( NULL );
6218     }
6219 return( PyFF_Glyph_get_layer_references((PyFF_Glyph *) PySC_From_SC(sc),NULL,layer));
6220 }
6221 
PyFF_RefArrayIndexAssign(PyObject * self,PyObject * index,PyObject * value)6222 static int PyFF_RefArrayIndexAssign( PyObject *self, PyObject *index, PyObject *value ) {
6223     SplineChar *sc = ((PyFF_RefArray *) self)->sc;
6224     int layer;
6225 
6226     if ( PyUnicode_Check(index)) {
6227 	const char *name = PyUnicode_AsUTF8(index);
6228 	if (name == NULL) {
6229 	    return -1;
6230 	}
6231 	layer = SFFindLayerIndexByName(sc->parent,name);
6232 	if ( layer<0 )
6233 return( -1 );
6234     } else if ( PyLong_Check(index)) {
6235 	layer = PyLong_AsLong(index);
6236     } else {
6237 	PyErr_Format(PyExc_TypeError, "Index must be a layer name or index" );
6238 return( -1 );
6239     }
6240 return( PyFF_Glyph_set_layer_references((PyFF_Glyph *) PySC_From_SC(sc),value,NULL,layer));
6241 }
6242 
6243 static PyMappingMethods PyFF_RefArrayMapping = {
6244     PyFF_RefArrayLength,		/* length */
6245     PyFF_RefArrayIndex,		/* subscript */
6246     PyFF_RefArrayIndexAssign	/* subscript assign */
6247 };
6248 
6249 /* ************************************************************************** */
6250 /* ************************* initializer routines *************************** */
6251 /* ************************************************************************** */
6252 
6253 static PyTypeObject PyFF_RefArrayType = {
6254     PyVarObject_HEAD_INIT(NULL, 0)
6255     "fontforge.references",    /* tp_name */
6256     sizeof(PyFF_RefArray),     /* tp_basicsize */
6257     0,                         /* tp_itemsize */
6258     (destructor) PyFF_RefArray_dealloc, /* tp_dealloc */
6259     0,                         /* tp_vectorcall_offset */
6260     NULL,                      /* tp_getattr */
6261     NULL,                      /* tp_setattr */
6262     NULL,                      /* tp_compare */
6263     NULL,                      /* tp_repr */
6264     NULL,                      /* tp_as_number */
6265     NULL,                      /* tp_as_sequence */
6266     &PyFF_RefArrayMapping,     /* tp_as_mapping */
6267     NULL,                      /* tp_hash */
6268     NULL,                      /* tp_call */
6269     (reprfunc) PyFFReferences_Str, /* tp_str */
6270     NULL,                      /* tp_getattro */
6271     NULL,                      /* tp_setattro */
6272     NULL,                      /* tp_as_buffer */
6273     Py_TPFLAGS_DEFAULT,        /* tp_flags */
6274     "FontForge layers references array",  /* tp_doc */
6275     NULL,                      /* tp_traverse */
6276     NULL,                      /* tp_clear */
6277     NULL,                      /* tp_richcompare */
6278     0,                         /* tp_weaklistoffset */
6279     layersiter_new,            /* tp_iter */
6280     NULL,                      /* tp_iternext */
6281     NULL,                      /* tp_methods */
6282     NULL,                      /* tp_members */
6283     NULL,                      /* tp_getset */
6284     NULL,                      /* tp_base */
6285     NULL,                      /* tp_dict */
6286     NULL,                      /* tp_descr_get */
6287     NULL,                      /* tp_descr_set */
6288     0,                         /* tp_dictoffset */
6289     NULL,                      /* tp_init */
6290     NULL,                      /* tp_alloc */
6291     NULL,                      /* tp_new */
6292     NULL,                      /* tp_free */
6293     NULL,                      /* tp_is_gc */
6294     NULL,                      /* tp_bases */
6295     NULL,                      /* tp_mro */
6296     NULL,                      /* tp_cache */
6297     NULL,                      /* tp_subclasses */
6298     NULL,                      /* tp_weaklist */
6299     NULL,                      /* tp_del */
6300     0,                         /* tp_version_tag */
6301 };
6302 
6303 /* ************************************************************************** */
6304 /*  Glyph Math Kerning  */
6305 /* ************************************************************************** */
6306 
PyFFMathKern_dealloc(PyFF_MathKern * self)6307 static void PyFFMathKern_dealloc(PyFF_MathKern *self) {
6308     Py_TYPE(self)->tp_free((PyObject*)self);
6309 }
6310 
PyFFMathKern_Str(PyFF_MathKern * self)6311 static PyObject *PyFFMathKern_Str(PyFF_MathKern *self) {
6312 return( PyUnicode_FromFormat( "<math kerning table for glyph %s>", self->sc->name ));
6313 }
6314 
PyFF_MathKern_get_kerns(PyFF_MathKern * self,void * closure)6315 static PyObject *PyFF_MathKern_get_kerns(PyFF_MathKern *self, void *closure) {
6316     struct mathkernvertex *mkv;
6317     PyObject *tuple;
6318     int i;
6319 
6320     if ( self->sc->mathkern==NULL )
6321 Py_RETURN_NONE;
6322     mkv = &self->sc->mathkern->top_right + (int) (intpt) closure;
6323     if ( mkv->cnt==0 )
6324 Py_RETURN_NONE;
6325 
6326     tuple = PyTuple_New(mkv->cnt);
6327     for ( i=0; i<mkv->cnt; ++i ) {
6328 	if ( i==mkv->cnt-1 )
6329 	    PyTuple_SetItem(tuple,i,Py_BuildValue( "(ii)", mkv->mkd[i].kern,self->sc->parent->ascent));
6330 	else
6331 	    PyTuple_SetItem(tuple,i,Py_BuildValue( "(ii)", mkv->mkd[i].kern,mkv->mkd[i].height));
6332     }
6333 return( tuple );
6334 }
6335 
PyFF_MathKern_set_kerns(PyFF_MathKern * self,PyObject * value,void * closure)6336 static int PyFF_MathKern_set_kerns(PyFF_MathKern *self, PyObject *value, void *closure) {
6337     struct mathkernvertex *mkv;
6338     struct mathkerndata *mkd;
6339     int i, cnt;
6340 
6341     if ( self->sc->mathkern==NULL ) {
6342 	if ( value==Py_None )
6343 return( 0 );
6344 	self->sc->mathkern = chunkalloc(sizeof(struct mathkern));
6345     }
6346     mkv = &self->sc->mathkern->top_right + (int) (intpt) closure;
6347     if ( value==Py_None ) {
6348 	MathKernVContentsFree(mkv);
6349 	mkv->cnt = 0;
6350 	mkv->mkd = NULL;
6351 return( 0 );
6352     }
6353     if ( !PyTuple_Check(value) && !PyList_Check(value)) {
6354 	PyErr_Format(PyExc_TypeError, "Value must be a tuple or a list" );
6355 return( -1 );
6356     }
6357     cnt = PySequence_Size(value);
6358     mkd = calloc(cnt,sizeof(struct mathkerndata));
6359     for ( i=0; i<cnt; ++i ) {
6360 	PyObject *obj = PySequence_GetItem(value,i);
6361 	if ( i==cnt-1 && PyLong_Check(obj))
6362 	    mkd[i].kern = PyLong_AsLong(obj);
6363 	else if ( !PyArg_ParseTuple(obj, "hh", &mkd[i].kern, &mkd[i].height )) {
6364 	    free(mkd);
6365 return( -1 );
6366 	}
6367     }
6368     MathKernVContentsFree(mkv);
6369     mkv->cnt = cnt;
6370     if ( cnt==0 ) {
6371 	free(mkd);
6372 	mkd=NULL;
6373     }
6374     mkv->mkd = mkd;
6375 return( 0 );
6376 }
6377 
6378 static PyGetSetDef PyFFMathKern_members[] = {
6379     {(char *)"topRight",
6380      (getter)PyFF_MathKern_get_kerns, (setter)PyFF_MathKern_set_kerns,
6381      (char *)"Math Kerning information for the top right corner", (void *) (intpt) 0},
6382     {(char *)"topLeft",
6383      (getter)PyFF_MathKern_get_kerns, (setter)PyFF_MathKern_set_kerns,
6384      (char *)"Math Kerning information for the top left corner", (void *) (intpt) 1},
6385     {(char *)"bottomLeft",
6386      (getter)PyFF_MathKern_get_kerns, (setter)PyFF_MathKern_set_kerns,
6387      (char *)"Math Kerning information for the bottom left corner", (void *) (intpt) 3},
6388     {(char *)"bottomRight",
6389      (getter)PyFF_MathKern_get_kerns, (setter)PyFF_MathKern_set_kerns,
6390      (char *)"Math Kerning information for the bottom right corner", (void *) (intpt) 2},
6391     PYGETSETDEF_EMPTY /* Sentinel */
6392 };
6393 
6394 static PyTypeObject PyFF_MathKernType = {
6395     PyVarObject_HEAD_INIT(NULL, 0)
6396     "fontforge.mathKern",      /* tp_name */
6397     sizeof(PyFF_MathKern),     /* tp_basicsize */
6398     0,                         /* tp_itemsize */
6399     (destructor)PyFFMathKern_dealloc, /* tp_dealloc */
6400     0,                         /* tp_vectorcall_offset */
6401     NULL,                      /* tp_getattr */
6402     NULL,                      /* tp_setattr */
6403     NULL,                      /* tp_compare */
6404     NULL,                      /* tp_repr */
6405     NULL,                      /* tp_as_number */
6406     NULL,                      /* tp_as_sequence */
6407     NULL,                      /* tp_as_mapping */
6408     NULL,                      /* tp_hash */
6409     NULL,                      /* tp_call */
6410     (reprfunc) PyFFMathKern_Str, /* tp_str */
6411     NULL,                      /* tp_getattro */
6412     NULL,                      /* tp_setattro */
6413     NULL,                      /* tp_as_buffer */
6414     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
6415     "fontforge per glyph math kerning objects", /* tp_doc */
6416     NULL,                      /* tp_traverse */
6417     NULL,                      /* tp_clear */
6418     NULL,                      /* tp_richcompare */
6419     0,                         /* tp_weaklistoffset */
6420     NULL,                      /* tp_iter */
6421     NULL,                      /* tp_iternext */
6422     NULL,                      /* tp_methods */
6423     NULL,                      /* tp_members */
6424     PyFFMathKern_members,      /* tp_getset */
6425     NULL,                      /* tp_base */
6426     NULL,                      /* tp_dict */
6427     NULL,                      /* tp_descr_get */
6428     NULL,                      /* tp_descr_set */
6429     0,                         /* tp_dictoffset */
6430     NULL,                      /* tp_init */
6431     NULL,                      /* tp_alloc */
6432     NULL,                      /* tp_new */
6433     NULL,                      /* tp_free */
6434     NULL,                      /* tp_is_gc */
6435     NULL,                      /* tp_bases */
6436     NULL,                      /* tp_mro */
6437     NULL,                      /* tp_cache */
6438     NULL,                      /* tp_subclasses */
6439     NULL,                      /* tp_weaklist */
6440     NULL,                      /* tp_del */
6441     0,                         /* tp_version_tag */
6442 };
6443 
6444 /* ************************************************************************** */
6445 /* Glyph Standard Methods */
6446 /* ************************************************************************** */
6447 
PyFF_Glyph_dealloc(PyFF_Glyph * self)6448 static void PyFF_Glyph_dealloc(PyFF_Glyph *self) {
6449     if ( self->sc!=NULL ) {
6450 	if ( self->sc->python_sc_object == self )
6451 	    self->sc->python_sc_object = NULL;
6452 	self->sc = NULL;
6453     }
6454     Py_XDECREF(self->layers);
6455     Py_XDECREF(self->refs);
6456     Py_XDECREF(self->mk);
6457     Py_TYPE(self)->tp_free((PyObject *) self);
6458 }
6459 
PyFFGlyph_Repr(PyFF_Glyph * self)6460 static PyObject *PyFFGlyph_Repr(PyFF_Glyph *self) {
6461     PyObject *ret;
6462     char buf[200];
6463     char *repr = buf;
6464     size_t space_needed;
6465     int at;
6466     const struct altuni *alt;
6467 
6468     /* Get space to hold string. For effeciency try to avoid malloc
6469      * except for rare cases where more is needed.
6470      */
6471     space_needed = 64;
6472     if ( self->sc!=NULL ) {
6473 	space_needed += strlen(self->sc->name);
6474 	/* Count the number of altuni where vs==-1, so we only get true alternates. */
6475 	for ( alt=self->sc->altuni; alt!=NULL; alt=alt->next )
6476 	    if ( alt->vs==-1 && alt->unienc>=0 )
6477 		space_needed += 9;
6478     }
6479     if ( space_needed >= sizeof(buf) )
6480 	repr = malloc( space_needed );
6481 
6482 #ifdef DEBUG
6483     at = sprintf(repr, "<%s at 0x%p sc=0x%p",
6484 		 Py_TYPENAME(self), self, self->sc);
6485 #else
6486     at = sprintf(repr, "<%s at 0x%p", Py_TYPENAME(self), self);
6487 #endif
6488     if ( self->sc==NULL ) {
6489 	strcpy( &repr[at], " CLOSED>" );
6490     }
6491     else {
6492 	if ( self->sc->unicodeenc >= 0 )
6493 	    at += sprintf( &repr[at], " U+%04X", self->sc->unicodeenc);
6494 
6495 	for ( alt=self->sc->altuni; alt!=NULL; alt=alt->next )
6496 	    if ( alt->vs==-1 && alt->unienc>=0 )
6497 		at += sprintf( &repr[at], " U+%04X", alt->unienc );
6498 
6499 	at += sprintf( &repr[at], " \"%s\">", self->sc->name);
6500     }
6501     ret = PyUnicode_FromString(repr);
6502     if ( repr != buf )
6503 	free(repr);
6504     return( ret );
6505 }
6506 
PyFFGlyph_Str(PyFF_Glyph * self)6507 static PyObject *PyFFGlyph_Str(PyFF_Glyph *self) {
6508     if ( self->sc==NULL || self->sc->parent==NULL )
6509 return( PyUnicode_FromString("<Glyph from closed font>") );
6510 return( PyUnicode_FromFormat( "<Glyph %s in font %s>", self->sc->name, self->sc->parent->fontname ));
6511 }
6512 
PyFFGlyph_docompare(PyFF_Glyph * self,PyObject * other,double pt_err,double spline_err)6513 static int PyFFGlyph_docompare(PyFF_Glyph *self,PyObject *other,
6514 	double pt_err, double spline_err) {
6515     SplineSet *ss2;
6516     int ret;
6517     SplinePoint *badpoint;
6518 
6519     if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(other)) ) {
6520 	SplineChar *sc = self->sc;
6521 	SplineChar *sc2 = ((PyFF_Glyph *) other)->sc;
6522 	int olayer = ((PyFF_Glyph *) other)->layer;
6523 	int ret;
6524 	SplinePoint *dummy;
6525 
6526 	ret = CompareLayer(NULL,
6527 		sc->layers[self->layer].splines,sc2->layers[olayer].splines,
6528 		sc->layers[self->layer].refs,sc2->layers[olayer].refs,
6529 		pt_err,spline_err,sc->name,false,&dummy);
6530 return( ret );
6531     } else if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(other)) ) {
6532 	ss2 = SSFromContour((PyFF_Contour *) other,NULL);
6533     } else if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(other)) ) {
6534 	ss2 = SSFromLayer((PyFF_Layer *) other);
6535     } else {
6536 	PyErr_Format(PyExc_TypeError, "Unexpected type");
6537 return( -1 );
6538     }
6539     if ( PyErr_Occurred() != NULL ) {
6540 	return( -1 );
6541     }
6542     if ( self->sc->layers[self->layer].refs!=NULL )
6543 return( SS_NoMatch | SS_RefMismatch );
6544     ret = SSsCompare(self->sc->layers[self->layer].splines,ss2,pt_err,spline_err,&badpoint);
6545     SplinePointListsFree(ss2);
6546 return(ret);
6547 }
6548 
PyFFGlyph_compare(PyFF_Glyph * self,PyObject * other)6549 static int PyFFGlyph_compare(PyFF_Glyph *self,PyObject *other) {
6550     const double pt_err = .5, spline_err = 1;
6551     int ret;
6552     SplineChar *sc1, *sc2;
6553 
6554     ret = PyFFGlyph_docompare(self,other,pt_err,spline_err);
6555     if ( !(ret&SS_NoMatch) )
6556 return( 0 );
6557 
6558     /* There's no real ordering on these guys. Make up something that is */
6559     /*  at least consistent */
6560     if ( !PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(other)) )
6561 return( -1 );
6562     /* Ok, both are glyphs */
6563     sc1 = self->sc; sc2 = ((PyFF_Glyph *) other)->sc;
6564 return( sc1<sc2 ? -1 : 1 );
6565 }
6566 
PyFFGlyph_richcompare(PyObject * a,PyObject * b,int op)6567 static PyObject *PyFFGlyph_richcompare(PyObject *a, PyObject *b, int op) {
6568     return enrichened_compare((cmpfunc) PyFFGlyph_compare, a, b, op);
6569 }
6570 
6571 /* ************************************************************************** */
6572 /* Glyph getters/setters */
6573 /* ************************************************************************** */
6574 
PyFF_Glyph_get_temporary(PyFF_Glyph * self,void * UNUSED (closure))6575 static PyObject *PyFF_Glyph_get_temporary(PyFF_Glyph *self, void *UNUSED(closure)) {
6576     if ( self->sc->python_temporary==NULL )
6577 Py_RETURN_NONE;
6578     Py_INCREF( (PyObject *) (self->sc->python_temporary) );
6579 return( self->sc->python_temporary );
6580 }
6581 
PyFF_Glyph_set_temporary(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6582 static int PyFF_Glyph_set_temporary(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6583     PyObject *old = self->sc->python_temporary;
6584 
6585     /* I'd rather not store None, because C routines don't understand it */
6586     /*  and they occasionally need to know whether there is something real */
6587     /*  in this field. */
6588     if ( value==Py_None )
6589 	value = NULL;
6590     Py_XINCREF(value);
6591     self->sc->python_temporary = value;
6592     Py_XDECREF(old);
6593 return( 0 );
6594 }
6595 
PyFF_Glyph_get_persistent(PyFF_Glyph * self,void * UNUSED (closure))6596 static PyObject *PyFF_Glyph_get_persistent(PyFF_Glyph *self, void *UNUSED(closure)) {
6597     if ( self->sc->layer_cnt <= ly_fore || self->sc->layers[ly_fore].python_persistent==NULL )
6598 Py_RETURN_NONE;
6599     Py_INCREF( (PyObject *) (self->sc->layers[ly_fore].python_persistent) );
6600 return( self->sc->layers[ly_fore].python_persistent );
6601 }
6602 
PyFF_Glyph_set_persistent(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6603 static int PyFF_Glyph_set_persistent(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6604     if ( self->sc->layer_cnt <= ly_fore ) return -1;
6605     PyObject *old = self->sc->layers[ly_fore].python_persistent;
6606 
6607     /* I'd rather not store None, because C routines don't understand it */
6608     /*  and they occasionally need to know whether there is something real */
6609     /*  in this field. */
6610     if ( value==Py_None )
6611 	value = NULL;
6612     Py_XINCREF(value);
6613     self->sc->layers[ly_fore].python_persistent = value;
6614     Py_XDECREF(old);
6615 return( 0 );
6616 }
6617 
PyFF_Glyph_get_activeLayer(PyFF_Glyph * self,void * UNUSED (closure))6618 static PyObject *PyFF_Glyph_get_activeLayer(PyFF_Glyph *self, void *UNUSED(closure)) {
6619 return( Py_BuildValue("i", self->layer ));
6620 }
6621 
PyFF_Glyph_set_activeLayer(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6622 static int PyFF_Glyph_set_activeLayer(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6623     int layer;
6624 
6625     if ( PyLong_Check(value) )
6626 	layer = PyLong_AsLong(value);
6627     else if ( PyUnicode_Check(value)) {
6628 	const char *name = PyUnicode_AsUTF8(value);
6629 	if (name == NULL) {
6630 	    return -1;
6631 	}
6632 	layer = SFFindLayerIndexByName(self->sc->parent,name);
6633 	if ( layer<0 )
6634 return( -1 );
6635     } else {
6636         return -1;
6637     }
6638     if ( layer<0 || layer>=self->sc->layer_cnt ) {
6639 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
6640 return( -1 );
6641     }
6642     self->layer = layer;
6643 return( 0 );
6644 }
6645 
PyFF_Glyph_get_glyphname(PyFF_Glyph * self,void * UNUSED (closure))6646 static PyObject *PyFF_Glyph_get_glyphname(PyFF_Glyph *self, void *UNUSED(closure)) {
6647 
6648 return( Py_BuildValue("s", self->sc->name ));
6649 }
6650 
PyFF_Glyph_set_glyphname(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6651 static int PyFF_Glyph_set_glyphname(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6652     FontViewBase *fvs;
6653     const char *str = PyUnicode_AsUTF8(value);
6654     if (str == NULL) {
6655         return -1;
6656     }
6657 
6658     SFGlyphRenameFixup(self->sc->parent,self->sc->name,str,false);
6659     self->sc->namechanged = self->sc->changed = true;
6660     free( self->sc->name );
6661     self->sc->name = copy(str);
6662     GlyphHashFree(self->sc->parent);
6663     SCRefreshTitles(self->sc);
6664     for ( fvs=self->sc->parent->fv; fvs!=NULL; fvs=fvs->nextsame ) {
6665 	/* Postscript encodings are by name, others are by codepoint */
6666 	if ( fvs->map->enc->psnames!=NULL && fvs->map->enc!=&custom ) {
6667 	    fvs->map->enc = &custom;
6668 	    FVSetTitle(fvs);
6669 	}
6670     }
6671 return( 0 );
6672 }
6673 
PyFF_Glyph_get_encoding(PyFF_Glyph * self,void * UNUSED (closure))6674 static PyObject *PyFF_Glyph_get_encoding(PyFF_Glyph *self, void *UNUSED(closure)) {
6675     SplineChar *sc = self->sc;
6676     EncMap *map = sc->parent->fv->map;
6677 
6678 return( Py_BuildValue("i", map->backmap[sc->orig_pos] ));
6679 }
6680 
PyFF_Glyph_get_codepoint(PyFF_Glyph * self,void * UNUSED (closure))6681 static PyObject *PyFF_Glyph_get_codepoint(PyFF_Glyph *self, void *UNUSED(closure)) {
6682     if ( self->sc==NULL || self->sc->unicodeenc < 0 )
6683 Py_RETURN_NONE;
6684     return PyUnicode_FromFormat("U+%04X", self->sc->unicodeenc);
6685 }
6686 
PyFF_Glyph_get_unicode(PyFF_Glyph * self,void * UNUSED (closure))6687 static PyObject *PyFF_Glyph_get_unicode(PyFF_Glyph *self, void *UNUSED(closure)) {
6688 
6689 return( Py_BuildValue("i", self->sc->unicodeenc ));
6690 }
6691 
PyFF_Glyph_set_unicode(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6692 static int PyFF_Glyph_set_unicode(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6693     FontViewBase *fvs;
6694     int uenc;
6695 
6696     uenc = PyLong_AsLong(value);
6697     if ( PyErr_Occurred()!=NULL )
6698 return( -1 );
6699     self->sc->unicodeenc = uenc;
6700     SCRefreshTitles(self->sc);
6701     for ( fvs=self->sc->parent->fv; fvs!=NULL; fvs=fvs->nextsame ) {
6702 	/* Postscript encodings are by name, others are by codepoint */
6703 	if ( fvs->map->enc->psnames==NULL && fvs->map->enc!=&custom ) {
6704 	    fvs->map->enc = &custom;
6705 	    FVSetTitle(fvs);
6706 	}
6707     }
6708 return( 0 );
6709 }
6710 
PyFF_Glyph_get_altuni(PyFF_Glyph * self,void * UNUSED (closure))6711 static PyObject *PyFF_Glyph_get_altuni(PyFF_Glyph *self, void *UNUSED(closure)) {
6712     int cnt;
6713     struct altuni *au;
6714     PyObject *ret;
6715 
6716     for ( cnt=0, au = self->sc->altuni; au!=NULL; au=au->next, ++cnt );
6717     if ( cnt==0 )
6718 Py_RETURN_NONE;
6719 
6720     ret = PyTuple_New(cnt);
6721     for ( cnt=0, au = self->sc->altuni; au!=NULL; au=au->next, ++cnt ) {
6722 	PyTuple_SET_ITEM(ret,cnt,Py_BuildValue("(iii)", au->unienc,
6723 		au->vs, au->fid));
6724     }
6725 return( ret );
6726 }
6727 
PyFF_Glyph_set_altuni(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6728 static int PyFF_Glyph_set_altuni(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6729     int cnt, i;
6730     struct altuni *head, *last=NULL, *cur;
6731     int uni, vs, fid;
6732     PyObject *obj;
6733     FontViewBase *fvs;
6734 
6735     if ( value == Py_None )
6736 	head = NULL;
6737     else if ( !PySequence_Check(value)) {
6738 	PyErr_Format(PyExc_TypeError, "Value must be a tuple of alternate unicode values");
6739 return( -1 );
6740     } else {
6741 	cnt = PySequence_Size(value);
6742 	for ( i=0; i<cnt; ++i ) {
6743 	    obj = PySequence_GetItem(value,i);
6744 	    uni = 0; vs = -1; fid = 0;
6745 	    if ( PyLong_Check(obj))
6746 		uni = PyLong_AsLong(obj);
6747 	    else if ( !PyArg_ParseTuple(obj,"i|ii", &uni, &vs, &fid))
6748 return( -1 );
6749 	    cur = chunkalloc(sizeof(struct altuni));
6750 	    if ( vs==0 ) vs=-1;		/* convention used in charinfo */
6751 	    cur->unienc = uni; cur->vs = vs; cur->fid = fid;
6752 	    if ( last == NULL )
6753 		head = cur;
6754 	    else
6755 		last->next = cur;
6756 	    last = cur;
6757 	}
6758     }
6759 
6760     AltUniFree(self->sc->altuni);
6761     self->sc->altuni = head;
6762 
6763     for ( fvs=self->sc->parent->fv; fvs!=NULL; fvs=fvs->nextsame ) {
6764 	fvs->map->enc = &custom;
6765 	FVSetTitle(fvs);
6766     }
6767 
6768     return( 0 );
6769 }
6770 
PyFF_Glyph_get_changed(PyFF_Glyph * self,void * UNUSED (closure))6771 static PyObject *PyFF_Glyph_get_changed(PyFF_Glyph *self, void *UNUSED(closure)) {
6772 
6773 return( Py_BuildValue("i", self->sc->changed ));
6774 }
6775 
PyFF_Glyph_set_changed(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6776 static int PyFF_Glyph_set_changed(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6777     int uenc;
6778 
6779     uenc = PyLong_AsLong(value);
6780     if ( PyErr_Occurred()!=NULL )
6781 return( -1 );
6782     self->sc->changed = uenc;
6783 return( 0 );
6784 }
6785 
PyFF_Glyph_get_texheight(PyFF_Glyph * self,void * UNUSED (closure))6786 static PyObject *PyFF_Glyph_get_texheight(PyFF_Glyph *self, void *UNUSED(closure)) {
6787 
6788 return( Py_BuildValue("i", self->sc->tex_height ));
6789 }
6790 
PyFF_Glyph_set_texheight(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6791 static int PyFF_Glyph_set_texheight(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6792     int val;
6793 
6794     val = PyLong_AsLong(value);
6795     if ( PyErr_Occurred()!=NULL )
6796 return( -1 );
6797     self->sc->tex_height = val;
6798 return( 0 );
6799 }
6800 
PyFF_Glyph_get_texdepth(PyFF_Glyph * self,void * UNUSED (closure))6801 static PyObject *PyFF_Glyph_get_texdepth(PyFF_Glyph *self, void *UNUSED(closure)) {
6802 
6803 return( Py_BuildValue("i", self->sc->tex_depth ));
6804 }
6805 
PyFF_Glyph_set_texdepth(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6806 static int PyFF_Glyph_set_texdepth(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6807     int val;
6808 
6809     val = PyLong_AsLong(value);
6810     if ( PyErr_Occurred()!=NULL )
6811 return( -1 );
6812     self->sc->tex_depth = val;
6813 return( 0 );
6814 }
6815 
PyFF_Glyph_get_italiccorrection(PyFF_Glyph * self,void * UNUSED (closure))6816 static PyObject *PyFF_Glyph_get_italiccorrection(PyFF_Glyph *self, void *UNUSED(closure)) {
6817 
6818 return( Py_BuildValue("i", self->sc->italic_correction ));
6819 }
6820 
PyFF_Glyph_set_italiccorrection(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6821 static int PyFF_Glyph_set_italiccorrection(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6822     int val;
6823 
6824     val = PyLong_AsLong(value);
6825     if ( PyErr_Occurred()!=NULL )
6826 return( -1 );
6827     self->sc->italic_correction = val;
6828 return( 0 );
6829 }
6830 
PyFF_Glyph_get_topaccent(PyFF_Glyph * self,void * UNUSED (closure))6831 static PyObject *PyFF_Glyph_get_topaccent(PyFF_Glyph *self, void *UNUSED(closure)) {
6832 
6833 return( Py_BuildValue("i", self->sc->top_accent_horiz ));
6834 }
6835 
PyFF_Glyph_set_topaccent(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6836 static int PyFF_Glyph_set_topaccent(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6837     int val;
6838 
6839     val = PyLong_AsLong(value);
6840     if ( PyErr_Occurred()!=NULL )
6841 return( -1 );
6842     self->sc->top_accent_horiz = val;
6843 return( 0 );
6844 }
6845 
PyFF_Glyph_get_isextendedshape(PyFF_Glyph * self,void * UNUSED (closure))6846 static PyObject *PyFF_Glyph_get_isextendedshape(PyFF_Glyph *self, void *UNUSED(closure)) {
6847     PyObject *ret;
6848 
6849     ret = self->sc->is_extended_shape ? Py_True : Py_False;
6850     Py_INCREF( ret );
6851 return( ret );
6852 }
6853 
PyFF_Glyph_set_isextendedshape(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6854 static int PyFF_Glyph_set_isextendedshape(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6855     int val;
6856 
6857     val = PyLong_AsLong(value);
6858     if ( PyErr_Occurred()!=NULL )
6859 return( -1 );
6860     self->sc->is_extended_shape = val!=0;
6861 return( 0 );
6862 }
6863 
6864 
PyFF_Glyph_get_unlinkRmOvrlpSave(PyFF_Glyph * self,void * UNUSED (closure))6865 static PyObject *PyFF_Glyph_get_unlinkRmOvrlpSave(PyFF_Glyph *self, void *UNUSED(closure)) {
6866 
6867 return( Py_BuildValue("i", self->sc->unlink_rm_ovrlp_save_undo ));
6868 }
6869 
PyFF_Glyph_set_unlinkRmOvrlpSave(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6870 static int PyFF_Glyph_set_unlinkRmOvrlpSave(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6871     int val;
6872 
6873     val = PyLong_AsLong(value);
6874     if ( PyErr_Occurred()!=NULL )
6875 return( -1 );
6876     self->sc->unlink_rm_ovrlp_save_undo = val;
6877 return( 0 );
6878 }
6879 
PyFF_Glyph_get_originalgid(PyFF_Glyph * self,void * UNUSED (closure))6880 static PyObject *PyFF_Glyph_get_originalgid(PyFF_Glyph *self, void *UNUSED(closure)) {
6881 
6882 return( Py_BuildValue("i", self->sc->orig_pos ));
6883 }
6884 
PyFF_Glyph_get_width(PyFF_Glyph * self,void * UNUSED (closure))6885 static PyObject *PyFF_Glyph_get_width(PyFF_Glyph *self, void *UNUSED(closure)) {
6886 
6887 return( Py_BuildValue("i", self->sc->width ));
6888 }
6889 
PyFF_Glyph_set_width(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6890 static int PyFF_Glyph_set_width(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6891     int val;
6892 
6893     val = PyLong_AsLong(value);
6894     if ( PyErr_Occurred()!=NULL )
6895 return( -1 );
6896     SCSynchronizeWidth(self->sc,val,self->sc->width,NULL);
6897     SCCharChangedUpdate(self->sc,ly_none);
6898 return( 0 );
6899 }
6900 
PyFF_Glyph_get_lsb(PyFF_Glyph * self,void * UNUSED (closure))6901 static PyObject *PyFF_Glyph_get_lsb(PyFF_Glyph *self, void *UNUSED(closure)) {
6902     DBounds b;
6903 
6904     SplineCharFindBounds(self->sc,&b);
6905 
6906 return( Py_BuildValue("d", b.minx ));
6907 }
6908 
PyFF_Glyph_set_lsb(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6909 static int PyFF_Glyph_set_lsb(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6910     int val;
6911     real trans[6];
6912     DBounds b;
6913     SplineChar *sc = self->sc;
6914 
6915     val = PyLong_AsLong(value);
6916     if ( PyErr_Occurred()!=NULL )
6917 return( -1 );
6918     SplineCharFindBounds(sc,&b);
6919 
6920     memset(trans,0,sizeof(trans));
6921     trans[0] = trans[3] = 1.0;
6922     trans[4] = val - b.minx;
6923     if ( trans[4]!=0 )
6924 	FVTrans(sc->parent->fv,sc,trans,NULL,fvt_alllayers);
6925 return( 0 );
6926 }
6927 
PyFF_Glyph_get_rsb(PyFF_Glyph * self,void * UNUSED (closure))6928 static PyObject *PyFF_Glyph_get_rsb(PyFF_Glyph *self, void *UNUSED(closure)) {
6929     DBounds b;
6930 
6931     SplineCharFindBounds(self->sc,&b);
6932 
6933 return( Py_BuildValue("d", self->sc->width - b.maxx ));
6934 }
6935 
PyFF_Glyph_set_rsb(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6936 static int PyFF_Glyph_set_rsb(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6937     int val;
6938     DBounds b;
6939 
6940     val = PyLong_AsLong(value);
6941     if ( PyErr_Occurred()!=NULL )
6942 return( -1 );
6943 
6944     SplineCharFindBounds(self->sc,&b);
6945     SCSynchronizeWidth(self->sc,rint( val+b.maxx ),self->sc->width,NULL);
6946     SCCharChangedUpdate(self->sc,ly_none);
6947 return( 0 );
6948 }
6949 
PyFF_Glyph_get_vwidth(PyFF_Glyph * self,void * UNUSED (closure))6950 static PyObject *PyFF_Glyph_get_vwidth(PyFF_Glyph *self, void *UNUSED(closure)) {
6951 
6952 return( Py_BuildValue("i", self->sc->vwidth ));
6953 }
6954 
PyFF_Glyph_set_vwidth(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6955 static int PyFF_Glyph_set_vwidth(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6956     int val;
6957 
6958     val = PyLong_AsLong(value);
6959     if ( PyErr_Occurred()!=NULL )
6960 return( -1 );
6961     self->sc->vwidth = val;
6962 return( 0 );
6963 }
6964 
PyFF_Glyph_get_manualhints(PyFF_Glyph * self,void * UNUSED (closure))6965 static PyObject *PyFF_Glyph_get_manualhints(PyFF_Glyph *self, void *UNUSED(closure)) {
6966 
6967 return( Py_BuildValue("i", self->sc->manualhints ));
6968 }
6969 
PyFF_Glyph_get_lcarets(PyFF_Glyph * self,void * UNUSED (closure))6970 static PyObject *PyFF_Glyph_get_lcarets(PyFF_Glyph *self, void *UNUSED(closure)) {
6971     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
6972     int cnt=0, i;
6973     PST *pst, *lcar = NULL;
6974     PyObject *tuple;
6975 
6976     for ( pst = sc->possub; pst!=NULL; pst=pst->next ) {
6977        if ( pst->type==pst_lcaret ) {
6978            lcar = pst;
6979            cnt = lcar->u.lcaret.cnt;
6980     break;
6981        }
6982     }
6983     tuple = PyTuple_New(cnt);
6984 
6985     if ( lcar != NULL ) {
6986        for ( i=0; i<cnt; ++i ) {
6987            PyTuple_SetItem( tuple,i,Py_BuildValue("i",lcar->u.lcaret.carets[i]) );
6988        }
6989     }
6990 return( tuple );
6991 }
6992 
PyFF_Glyph_set_lcarets(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))6993 static int PyFF_Glyph_set_lcarets(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
6994     SplineChar *sc = self->sc;
6995     int i, cnt, lig_comp_max = 0, lc;
6996     char *pt;
6997     int16 *carets;
6998     PST *pst, *lcar = NULL;
6999 
7000     cnt = PySequence_Size(value);
7001     if ( cnt==-1 )
7002 return( -1 );
7003 
7004     if ( cnt > 0 )
7005        carets = malloc( cnt*sizeof(int16) );
7006     for ( i=0; i<cnt; ++i ) {
7007        carets[i] = PyLong_AsLong( PySequence_GetItem(value,i) );
7008        if ( PyErr_Occurred()) {
7009            free(carets);
7010 return( -1 );
7011        }
7012     }
7013 
7014     for ( pst = sc->possub; pst!=NULL; pst=pst->next ) {
7015        if ( pst->type==pst_lcaret ) {
7016            lcar = pst;
7017            free( lcar->u.lcaret.carets );
7018        } else if ( pst->type==pst_ligature ) {
7019            for ( lc=0, pt=pst->u.lig.components; *pt; ++pt )
7020                if ( *pt==' ' ) ++lc;
7021            if ( lc>lig_comp_max )
7022                lig_comp_max = lc;
7023        }
7024     }
7025 
7026     if ( lcar == NULL && cnt > 0 ) {
7027        lcar = chunkalloc(sizeof(PST));
7028        lcar->type = pst_lcaret;
7029        lcar->next = sc->possub;
7030        sc->possub = lcar;
7031     }
7032     if ( lcar != NULL ) {
7033        lcar->u.lcaret.cnt = cnt;
7034        lcar->u.lcaret.carets = cnt > 0 ? carets : NULL;
7035        sc->lig_caret_cnt_fixed = ( cnt != lig_comp_max ) ? true : false;
7036     }
7037 return( 0 );
7038 }
7039 
PyFF_Glyph_set_manualhints(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7040 static int PyFF_Glyph_set_manualhints(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7041     int val;
7042 
7043     val = PyLong_AsLong(value);
7044     if ( PyErr_Occurred()!=NULL )
7045 return( -1 );
7046     self->sc->manualhints = (val!=0);
7047 return( 0 );
7048 }
7049 
PyFF_Glyph_get_font(PyFF_Glyph * self,void * UNUSED (closure))7050 static PyObject *PyFF_Glyph_get_font(PyFF_Glyph *self, void *UNUSED(closure)) {
7051     PyFF_Font *font = PyFF_FontForSC(self->sc);
7052     if ( font==NULL )
7053 Py_RETURN_NONE;
7054     Py_INCREF(font);
7055     return (PyObject*)font;
7056 }
7057 
PyFF_Glyph_get_references(PyFF_Glyph * self,void * closure)7058 static PyObject *PyFF_Glyph_get_references(PyFF_Glyph *self, void *closure) {
7059 return( PyFF_Glyph_get_layer_references(self,closure,self->layer));
7060 }
7061 
PyFF_Glyph_set_references(PyFF_Glyph * self,PyObject * value,void * closure)7062 static int PyFF_Glyph_set_references(PyFF_Glyph *self,PyObject *value, void *closure) {
7063 return( PyFF_Glyph_set_layer_references(self,value,closure,self->layer));
7064 }
7065 
PyFF_Glyph_get_layerrefs(PyFF_Glyph * self,void * UNUSED (closure))7066 static PyObject *PyFF_Glyph_get_layerrefs(PyFF_Glyph *self, void *UNUSED(closure)) {
7067     PyFF_RefArray *layerrefs;
7068 
7069     if ( self->refs!=NULL )
7070 Py_RETURN( self->refs );
7071     layerrefs = (PyFF_RefArray *) PyObject_New(PyFF_RefArray, &PyFF_RefArrayType);
7072     if (layerrefs == NULL)
7073 return NULL;
7074     layerrefs->sc = self->sc;
7075     self->refs = layerrefs;
7076 Py_RETURN( self->refs );
7077 }
7078 
PyFF_Glyph_get_ttfinstrs(PyFF_Glyph * self,void * UNUSED (closure))7079 static PyObject *PyFF_Glyph_get_ttfinstrs(PyFF_Glyph *self, void *UNUSED(closure)) {
7080     SplineChar *sc = self->sc;
7081     PyObject *binstr;
7082 
7083     binstr = PyBytes_FromStringAndSize((char *) sc->ttf_instrs,sc->ttf_instrs_len);
7084 return( binstr );
7085 }
7086 
PyFF_Glyph_set_ttfinstrs(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7087 static int PyFF_Glyph_set_ttfinstrs(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7088     int i, cnt;
7089     SplineChar *sc = self->sc;
7090 
7091     if ( !PySequence_Check(value)) {
7092 	PyErr_Format(PyExc_TypeError, "Value must be a sequence of integers");
7093 return( -1 );
7094     }
7095     cnt = PySequence_Size(value);
7096     free(sc->ttf_instrs); sc->ttf_instrs = NULL; sc->ttf_instrs_len = cnt;
7097     SCNumberPoints(sc,self->layer);	/* If the point numbering is wrong then we'll just throw away the instructions when we notice it */
7098     sc->instructions_out_of_date = false;
7099     if ( cnt==0 )
7100 return( 0 );
7101     if ( PyBytes_Check(value)) {
7102 	char *space; Py_ssize_t len;
7103 	PyBytes_AsStringAndSize(value,&space,&len);
7104 	sc->ttf_instrs = calloc(len,sizeof(uint8));
7105 	sc->ttf_instrs_len = len;
7106 	memcpy(sc->ttf_instrs,space,len);
7107     } else {
7108 	sc->ttf_instrs = calloc(cnt,sizeof(uint8));
7109 	for ( i=0; i<cnt; ++i ) {
7110 	    int val = PyLong_AsLong(PySequence_GetItem(value,i));
7111 	    if ( PyErr_Occurred()!=NULL )
7112 return( -1 );
7113 	    sc->ttf_instrs[i] = val;
7114 	}
7115     }
7116 return( 0 );
7117 }
7118 
7119 struct flaglist glyphclasses[] = {
7120     { "automatic", 0 },
7121     { "noclass", 1 },
7122     { "baseglyph", 2 },
7123     { "baseligature", 3 },
7124     { "mark", 4 },
7125     { "component", 5 },
7126     FLAGLIST_EMPTY /* Sentinel */
7127 };
7128 
PyFF_Glyph_get_glyphclass(PyFF_Glyph * self,void * UNUSED (closure))7129 static PyObject *PyFF_Glyph_get_glyphclass(PyFF_Glyph *self, void *UNUSED(closure)) {
7130 return( Py_BuildValue("s", glyphclasses[self->sc->glyph_class].name ));
7131 }
7132 
PyFF_Glyph_set_glyphclass(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7133 static int PyFF_Glyph_set_glyphclass(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7134     int gc;
7135     const char *glyphclassname = PyUnicode_AsUTF8(value);
7136 
7137     if (glyphclassname == NULL) {
7138         return -1;
7139     }
7140     gc = FlagsFromString(glyphclassname,glyphclasses,"glyph class");
7141     if ( gc==FLAG_UNKNOWN )
7142         return( -1 );
7143     self->sc->glyph_class = gc;
7144     return( 0 );
7145 }
7146 
PyFF_Glyph_get_layers(PyFF_Glyph * self,void * UNUSED (closure))7147 static PyObject *PyFF_Glyph_get_layers(PyFF_Glyph *self, void *UNUSED(closure)) {
7148     PyFF_LayerArray *layers;
7149 
7150     if ( self->layers!=NULL )
7151 Py_RETURN( self->layers );
7152     layers = (PyFF_LayerArray *) PyObject_New(PyFF_LayerArray, &PyFF_LayerArrayType);
7153     if (layers == NULL)
7154 return NULL;
7155     layers->sc = self->sc;
7156     self->layers = layers;
7157 Py_RETURN( self->layers );
7158 }
7159 
PyFF_Glyph_get_layer_cnt(PyFF_Glyph * self,void * UNUSED (closure))7160 static PyObject *PyFF_Glyph_get_layer_cnt(PyFF_Glyph *self, void *UNUSED(closure)) {
7161 
7162 return( Py_BuildValue("i", self->sc->layer_cnt ));
7163 }
7164 
PyFF_Glyph_get_hints(StemInfo * head)7165 static PyObject *PyFF_Glyph_get_hints(StemInfo *head) {
7166     StemInfo *h;
7167     int cnt;
7168     PyObject *tuple;
7169 
7170     for ( h=head, cnt=0; h!=NULL; h=h->next, ++cnt );
7171     tuple = PyTuple_New(cnt);
7172     for ( h=head, cnt=0; h!=NULL; h=h->next, ++cnt ) {
7173 	double start, width;
7174 	start = h->start; width = h->width;
7175 	if ( h->ghost && width>0 ) {
7176 	    start += width;
7177 	    width = -width;
7178 	}
7179 	PyTuple_SetItem(tuple,cnt,Py_BuildValue("(dd)", start, width ));
7180     }
7181 
7182 return( tuple );
7183 }
7184 
PyFF_Glyph_set_hints(PyFF_Glyph * self,int is_v,PyObject * value)7185 static int PyFF_Glyph_set_hints(PyFF_Glyph *self,int is_v,PyObject *value) {
7186     SplineChar *sc = self->sc;
7187     StemInfo *head=NULL, *tail=NULL, *cur;
7188     int i, cnt;
7189     double start, width;
7190     StemInfo **_head = is_v ? &sc->vstem : &sc->hstem;
7191     int layer;
7192 
7193     cnt = PySequence_Size(value);
7194     if ( cnt==-1 )
7195 return( -1 );
7196     for ( i=0; i<cnt; ++i ) {
7197 	if ( !PyArg_ParseTuple(PySequence_GetItem(value,i),"dd", &start, &width ))
7198 return( -1 );
7199 	cur = chunkalloc(sizeof(StemInfo));
7200 	if ( width==-20 || width==-21 )
7201 	    cur->ghost = true;
7202 	if ( width<0 ) {
7203 	    start += width;
7204 	    width = -width;
7205 	}
7206 	cur->start = start;
7207 	cur->width = width;
7208 	if ( tail==NULL )
7209 	    head = cur;
7210 	else
7211 	    tail->next = cur;
7212 	tail = cur;
7213     }
7214 
7215     StemInfosFree(*_head);
7216     for ( layer=ly_fore; layer<sc->layer_cnt; ++layer )
7217 	SCClearHintMasks(sc,layer,true);
7218     *_head = HintCleanup(head,true,1);
7219     if ( is_v ) {
7220         sc->vstem = head;
7221         SCGuessHintInstancesList( sc,self->layer,NULL,sc->vstem,NULL,false,false );
7222 	sc->vconflicts = StemListAnyConflicts(sc->vstem);
7223     } else {
7224         sc->hstem = head;
7225         SCGuessHintInstancesList( sc,self->layer,sc->hstem,NULL,NULL,false,false );
7226 	sc->hconflicts = StemListAnyConflicts(sc->hstem);
7227     }
7228 
7229     SCCharChangedUpdate(sc,ly_none);
7230 return( 0 );
7231 }
7232 
PyFF_Glyph_get_dhints(PyFF_Glyph * self,void * UNUSED (closure))7233 static PyObject *PyFF_Glyph_get_dhints(PyFF_Glyph *self, void *UNUSED(closure)) {
7234     DStemInfo *ds, *dn;
7235     int cnt;
7236     PyObject *tuple;
7237 
7238     ds = self->sc->dstem;
7239     for ( dn=ds, cnt=0; dn!=NULL; dn=dn->next, ++cnt );
7240     tuple = PyTuple_New(cnt);
7241     for ( dn=ds, cnt=0; dn!=NULL; dn=dn->next, ++cnt ) {
7242 	BasePoint left, right, unit;
7243 	left = dn->left; right = dn->right;
7244         unit = dn->unit;
7245 	PyTuple_SetItem(tuple,cnt,Py_BuildValue("((dd)(dd)(dd))",
7246             left.x,left.y,right.x,right.y,unit.x,unit.y ));
7247     }
7248 
7249 return( tuple );
7250 }
7251 
PyFF_Glyph_set_dhints(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7252 static int PyFF_Glyph_set_dhints(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7253     SplineChar *sc = self->sc;
7254     DStemInfo *head=NULL, *cur;
7255     int i, cnt;
7256     double len, width;
7257     double lx, ly, rx, ry, ux, uy;
7258     DStemInfo **_head = &sc->dstem;
7259 
7260     cnt = PySequence_Size(value);
7261     if ( cnt==-1 )
7262 return( -1 );
7263     for ( i=0; i<cnt; ++i ) {
7264 	if ( !PyArg_ParseTuple(PySequence_GetItem(value,i),"(dd)(dd)(dd)",
7265             &lx,&ly,&rx,&ry,&ux,&uy ))
7266 return( -1 );
7267         if ( ux == 0 && uy == 0 ) {
7268             LogError(_("Invalid unit vector has been specified. The hint is ignored.\n"));
7269     continue;
7270         } else if ( ux == 0 ) {
7271             LogError(_("Use the \'vhint\' property to specify a vertical hint.\n"));
7272     continue;
7273         } else if ( uy == 0 ) {
7274             LogError(_("Use the \'hhint\' property to specify a horizontal hint.\n"));
7275     continue;
7276         }
7277 	cur = chunkalloc(sizeof(DStemInfo));
7278         len = sqrt( pow( ux,2 ) + pow( uy,2 ));
7279         ux /= len; uy /= len;
7280         if ( ux < 0 ) {
7281             cur->unit.x = -ux; cur->unit.y = -uy;
7282         } else {
7283             cur->unit.x = ux; cur->unit.y = uy;
7284         }
7285         width = ( rx - lx )*cur->unit.y - ( ry - ly )*cur->unit.x;
7286         if ( width < 0 ) {
7287 	    cur->left.x = lx; cur->left.y = ly;
7288 	    cur->right.x = rx; cur->right.y = ry;
7289         } else {
7290 	    cur->left.x = rx; cur->left.y = ry;
7291 	    cur->right.x = lx; cur->right.y = ly;
7292         }
7293         MergeDStemInfo( sc->parent,&head,cur );
7294     }
7295 
7296     DStemInfosFree(*_head);
7297     sc->dstem = head;
7298     SCGuessHintInstancesList( sc,self->layer,NULL,NULL,sc->dstem,false,true );
7299     SCCharChangedUpdate(sc,ly_none);
7300 return( 0 );
7301 }
7302 
PyFF_Glyph_get_hhints(PyFF_Glyph * self,void * UNUSED (closure))7303 static PyObject *PyFF_Glyph_get_hhints(PyFF_Glyph *self, void *UNUSED(closure)) {
7304 return( PyFF_Glyph_get_hints(self->sc->hstem));
7305 }
7306 
PyFF_Glyph_set_hhints(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7307 static int PyFF_Glyph_set_hhints(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7308 return( PyFF_Glyph_set_hints(self,false,value));
7309 }
7310 
PyFF_Glyph_get_vhints(PyFF_Glyph * self,void * UNUSED (closure))7311 static PyObject *PyFF_Glyph_get_vhints(PyFF_Glyph *self, void *UNUSED(closure)) {
7312 return( PyFF_Glyph_get_hints(self->sc->vstem));
7313 }
7314 
PyFF_Glyph_set_vhints(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7315 static int PyFF_Glyph_set_vhints(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7316 return( PyFF_Glyph_set_hints(self,true,value));
7317 }
7318 
PyFF_Glyph_get_user_decomp(PyFF_Glyph * self,void * UNUSED (closure))7319 static PyObject *PyFF_Glyph_get_user_decomp(PyFF_Glyph *self, void *UNUSED(closure)) {
7320     PyObject *ret;
7321 
7322     if ( self->sc->user_decomp==NULL ) {
7323         return( Py_BuildValue("s", "" ));
7324     }
7325     else {
7326         char* out = u2utf8_copy(self->sc->user_decomp);
7327         ret = PyUnicode_DecodeUTF8(out, strlen(out), NULL);
7328         free(out);
7329         return ret;
7330     }
7331 }
7332 
PyFF_Glyph_set_user_decomp(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7333 static int PyFF_Glyph_set_user_decomp(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7334     const char *temp;
7335     unichar_t *udbuf;
7336 
7337     if (value == Py_None) {
7338         if (self->sc->user_decomp != NULL) { free(self->sc->user_decomp); }
7339         self->sc->user_decomp = NULL;
7340         return 0;
7341     } else if ((temp = PyUnicode_AsUTF8(value)) == NULL) {
7342         return -1;
7343     }
7344 
7345     udbuf = utf82u_copy(temp);
7346 
7347     if ( udbuf==NULL ) return -1;
7348 
7349     if (udbuf[0] == '\0') {
7350         if (self->sc->user_decomp != NULL) { free(self->sc->user_decomp); }
7351         self->sc->user_decomp = NULL;
7352         return 0;
7353     }
7354 
7355     if (self->sc->user_decomp != NULL) { free(self->sc->user_decomp); }
7356     self->sc->user_decomp = udbuf;
7357 
7358     return 0;
7359 }
7360 
PyFF_Glyph_get_comment(PyFF_Glyph * self,void * UNUSED (closure))7361 static PyObject *PyFF_Glyph_get_comment(PyFF_Glyph *self, void *UNUSED(closure)) {
7362     if ( self->sc->comment==NULL )
7363 return( Py_BuildValue("s", "" ));
7364     else
7365 return( PyUnicode_DecodeUTF8(self->sc->comment,strlen(self->sc->comment),NULL));
7366 }
7367 
PyFF_Glyph_set_comment(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7368 static int PyFF_Glyph_set_comment(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7369     char *newv = copy(PyUnicode_AsUTF8(value));
7370     if ( newv==NULL )
7371 return( -1 );
7372     free(self->sc->comment);
7373     self->sc->comment = newv;
7374 return( 0 );
7375 }
7376 
7377 /* Anchor type: see 'enum anchor_type' in splinefont.h */
7378 static struct flaglist ap_types[] = {
7379     { "mark", at_mark },
7380     { "base", at_basechar },
7381     { "ligature", at_baselig },
7382     { "basemark", at_basemark },
7383     { "entry", at_centry },
7384     { "exit", at_cexit },
7385     { "baselig", at_baselig },
7386     FLAGLIST_EMPTY /* Sentinel */
7387 };
7388 
_PyFF_Glyph_get_anchorPoints(PyFF_Glyph * self,int withsel)7389 static PyObject *_PyFF_Glyph_get_anchorPoints(PyFF_Glyph *self,int withsel) {
7390     SplineChar *sc = self->sc;
7391     AnchorPoint *ap;
7392     int cnt;
7393     PyObject *tuple;
7394 
7395     for ( ap=sc->anchor, cnt=0; ap!=NULL; ap=ap->next, ++cnt );
7396     tuple = PyTuple_New(cnt);
7397     for ( ap=sc->anchor, cnt=0; ap!=NULL; ap=ap->next, ++cnt ) {
7398 	if ( !withsel ) {
7399 	    if ( ap->type == at_baselig )
7400 		PyTuple_SetItem(tuple,cnt,Py_BuildValue("(ssddi)", ap->anchor->name,
7401 			ap_types[ap->type].name, ap->me.x, ap->me.y, ap->lig_index ));
7402 	    else
7403 		PyTuple_SetItem(tuple,cnt,Py_BuildValue("(ssdd)", ap->anchor->name,
7404 			ap_types[ap->type].name, ap->me.x, ap->me.y ));
7405 	} else {
7406 	    if ( ap->type == at_baselig )
7407 		PyTuple_SetItem(tuple,cnt,Py_BuildValue("(ssddOi)", ap->anchor->name,
7408 			ap_types[ap->type].name, ap->me.x, ap->me.y,
7409 			ap->selected?Py_True:Py_False, ap->lig_index ));
7410 	    else
7411 		PyTuple_SetItem(tuple,cnt,Py_BuildValue("(ssddO)", ap->anchor->name,
7412 			ap_types[ap->type].name, ap->me.x, ap->me.y,
7413 			ap->selected?Py_True:Py_False));
7414 	}
7415     }
7416 
7417 return( tuple );
7418 }
7419 
PyFF_Glyph_get_anchorPoints(PyFF_Glyph * self,void * UNUSED (closure))7420 static PyObject *PyFF_Glyph_get_anchorPoints(PyFF_Glyph *self, void *UNUSED(closure)) {
7421 return( _PyFF_Glyph_get_anchorPoints(self,false));
7422 }
7423 
PyFF_Glyph_get_anchorPointsWithSel(PyFF_Glyph * self,void * UNUSED (closure))7424 static PyObject *PyFF_Glyph_get_anchorPointsWithSel(PyFF_Glyph *self, void *UNUSED(closure)) {
7425 return( _PyFF_Glyph_get_anchorPoints(self,true));
7426 }
7427 
APFromTuple(SplineChar * sc,PyObject * tuple)7428 static AnchorPoint *APFromTuple(SplineChar *sc,PyObject *tuple) {
7429     char *ac_name, *type;
7430     double x, y;
7431     int lig_index=-1, selected=false;
7432     AnchorPoint *ap;
7433     AnchorClass *ac;
7434     SplineFont *sf = sc->parent;
7435     int aptype;
7436     int len, hassel=false;
7437 
7438     len = PyTuple_Size(tuple);
7439     if ( len==5 ) {
7440 	PyObject *o = PyTuple_GetItem(tuple,4);
7441 	if ( PyBool_Check(o))
7442 	    hassel = true;
7443     } else if ( len==6 )
7444 	hassel = true;
7445 
7446     if ( hassel ) {
7447 	if ( !PyArg_ParseTuple(tuple, "ssdd|ii", &ac_name, &type, &x, &y,
7448 		&selected, &lig_index ))
7449 return( NULL );
7450     } else {
7451 	if ( !PyArg_ParseTuple(tuple, "ssdd|i", &ac_name, &type, &x, &y, &lig_index ))
7452 return( NULL );
7453     }
7454     aptype = FlagsFromString(type,ap_types,"anchor type");
7455     if ( aptype==FLAG_UNKNOWN )
7456 return( NULL );
7457     for ( ac=sf->anchor; ac!=NULL; ac=ac->next ) {
7458 	if ( strcmp(ac->name,ac_name)==0 )
7459     break;
7460     }
7461     if ( ac==NULL ) {
7462         ac = chunkalloc(sizeof(AnchorClass));
7463         ac->name = copy( ac_name );
7464         ac->subtable = NULL;
7465         ac->type = act_unknown;
7466         ac->next = sf->anchor;
7467         sf->anchor = ac;
7468     }
7469     switch ( ac->type ) {
7470       case act_mark:
7471 	if ( aptype!=at_mark && aptype!=at_basechar ) {
7472 	    PyErr_Format(PyExc_TypeError, "You must specify either a mark or a base anchor type for this anchor class, %s.", ac_name );
7473 return( NULL );
7474 	}
7475       break;
7476       case act_mkmk:
7477 	if ( aptype!=at_mark && aptype!=at_basemark ) {
7478 	    PyErr_Format(PyExc_TypeError, "You must specify either a mark or a base mark anchor type for this anchor class, %s.", ac_name );
7479 return( NULL );
7480 	}
7481       break;
7482       case act_mklg:
7483 	if ( aptype!=at_mark && aptype!=at_baselig ) {
7484 	    PyErr_Format(PyExc_TypeError, "You must specify either a mark or a ligature anchor type for this anchor class, %s.", ac_name );
7485 return( NULL );
7486 	}
7487       break;
7488       case act_curs:
7489 	if ( aptype!=at_centry && aptype!=at_cexit ) {
7490 	    PyErr_Format(PyExc_TypeError, "You must specify either an entry or an exit anchor type for this anchor class, %s.", ac_name );
7491 return( NULL );
7492 	}
7493       default:
7494       break;
7495       /* leave act_unknown to allow anything until we resolve it by associating with a subtable */
7496     }
7497     if ( lig_index==-1 && aptype==at_baselig ) {
7498 	PyErr_Format(PyExc_TypeError, "You must specify a ligature index for a ligature anchor point" );
7499 return( NULL );
7500     } else if ( lig_index!=-1 && aptype!=at_baselig ) {
7501 	PyErr_Format(PyExc_TypeError, "You may not specify a ligature index for a non-ligature anchor point" );
7502 return( NULL );
7503     }
7504 
7505     ap = chunkalloc(sizeof(AnchorPoint));
7506     ap->anchor = ac;
7507     ap->type = aptype;
7508     ap->me.x = x;
7509     ap->me.y = y;
7510     ap->selected = selected;
7511     if ( aptype==at_baselig )
7512 	ap->lig_index = lig_index;
7513 return( ap );
7514 }
7515 
PyFF_Glyph_set_anchorPoints(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7516 static int PyFF_Glyph_set_anchorPoints(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7517     AnchorPoint *aphead=NULL, *aplast = NULL, *ap;
7518     int i;
7519     SplineChar *sc = self->sc;
7520 
7521     if ( !PySequence_Check(value)) {
7522 	PyErr_Format(PyExc_TypeError, "Expected a tuple of anchor points" );
7523 return( -1 );
7524     }
7525 
7526     for ( i=0; i<PySequence_Size(value); ++i ) {
7527 	ap = APFromTuple(sc,PySequence_GetItem(value,i));
7528 	if ( ap==NULL )
7529 return( -1 );
7530 	if ( aphead==NULL )
7531 	    aphead = ap;
7532 	else
7533 	    aplast->next = ap;
7534 	aplast = ap;
7535     }
7536     AnchorPointsFree(sc->anchor);
7537     sc->anchor = aphead;
7538     SCCharChangedUpdate(sc,ly_none);
7539 return( 0 );
7540 }
7541 
PyFF_Glyph_get_color(PyFF_Glyph * self,void * UNUSED (closure))7542 static PyObject *PyFF_Glyph_get_color(PyFF_Glyph *self, void *UNUSED(closure)) {
7543 return( Py_BuildValue("i", self->sc->color ));
7544 }
7545 
PyFF_Glyph_set_color(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7546 static int PyFF_Glyph_set_color(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7547     int val;
7548 
7549     val = PyLong_AsLong(value);
7550     if ( PyErr_Occurred()!=NULL )
7551 return( -1 );
7552     self->sc->color = val;
7553 return( 0 );
7554 }
7555 
PyFF_Glyph_get_script(PyFF_Glyph * self,void * UNUSED (closure))7556 static PyObject *PyFF_Glyph_get_script(PyFF_Glyph *self, void *UNUSED(closure)) {
7557     uint32 script = SCScriptFromUnicode(self->sc);
7558 
7559 return( TagToPythonString(script, false ));
7560 }
7561 
PyFF_Glyph_get_validation_state(PyFF_Glyph * self,void * UNUSED (closure))7562 static PyObject *PyFF_Glyph_get_validation_state(PyFF_Glyph *self, void *UNUSED(closure)) {
7563 return( Py_BuildValue("i", self->sc->layers[self->layer].validation_state ));
7564 }
7565 
GlyphListToStr(PyObject * value)7566 static char *GlyphListToStr(PyObject *value) {
7567     char *str, *pt;
7568     int i,cnt,len;
7569 
7570     if ( !PySequence_Check(value)) {
7571 	PyErr_Format(PyExc_TypeError, "Value must be a sequence" );
7572 return( NULL );
7573     }
7574     if ( PyUnicode_Check(value) ) {
7575         str = copy(PyUnicode_AsUTF8(value));
7576     } else {
7577 	cnt = PySequence_Size(value);
7578 	len = 0;
7579 	for ( i=0; i<cnt; ++i ) {
7580 	    PyObject *obj = PySequence_GetItem(value,i);
7581 	    if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(obj)) ) {
7582 		PyFF_Glyph *g = (PyFF_Glyph *) obj;
7583 		len += strlen(g->sc->name) + 1;
7584 	    } else {
7585 		Py_XDECREF(obj);
7586 		PyErr_Format(PyExc_TypeError, "Must be a sequence of glyphs" );
7587 return( NULL );
7588 	    }
7589 	    Py_DECREF(obj);
7590 	}
7591 	pt = str = malloc(len+1);
7592 	for ( i=0; i<cnt; ++i ) {
7593 	    PyObject *obj = PySequence_GetItem(value,i);
7594 	    PyFF_Glyph *g = (PyFF_Glyph *) obj;
7595 	    strcpy(pt,g->sc->name);
7596 	    pt += strlen(g->sc->name);
7597 	    strcpy(pt," ");
7598 	    pt += 1;
7599 	    Py_DECREF(obj);
7600 	}
7601 	if ( pt>str ) pt[-1] = '\0';
7602     }
7603 return( str );
7604 }
7605 
FreeGVPartsList(struct gv_part * parts,int cnt)7606 static void FreeGVPartsList(struct gv_part* parts, int cnt) {
7607     if (parts == NULL) {
7608         return;
7609     }
7610     while (--cnt >= 0) {
7611         free(parts[cnt].component);
7612     }
7613     free(parts);
7614 }
7615 
FreeGVParts(struct glyphvariants * gv)7616 static void FreeGVParts(struct glyphvariants *gv) {
7617     if (gv != NULL && gv->part_cnt != 0) {
7618         FreeGVPartsList(gv->parts, gv->part_cnt);
7619         gv->part_cnt = 0;
7620         gv->parts = NULL;
7621     }
7622 }
7623 
BuildComponentTuple(struct glyphvariants * gv)7624 static PyObject *BuildComponentTuple(struct glyphvariants *gv) {
7625     PyObject *tuple;
7626     int i;
7627 
7628     if ( gv->part_cnt==0 )
7629 Py_RETURN_NONE;
7630     tuple = PyTuple_New(gv->part_cnt);
7631     for ( i=0; i<gv->part_cnt; ++i ) {
7632 	PyTuple_SetItem(tuple,i,Py_BuildValue("(siiii)",
7633 		gv->parts[i].component,
7634 		gv->parts[i].is_extender,
7635 		gv->parts[i].startConnectorLength,
7636 		gv->parts[i].endConnectorLength,
7637 		gv->parts[i].fullAdvance));
7638     }
7639 return( tuple );
7640 }
7641 
ParseComponentTuple(PyObject * tuple,int * _cnt)7642 static struct gv_part *ParseComponentTuple(PyObject *tuple,int *_cnt) {
7643     int i, cnt;
7644     struct gv_part *parts;
7645     PyObject *seq = PySequence_Fast(tuple, "Must be a tuple or a list");
7646 
7647     if (seq == NULL) {
7648         return NULL;
7649     }
7650 
7651     *_cnt = cnt = PySequence_Fast_GET_SIZE(seq);
7652     parts = calloc(cnt+1,sizeof(struct gv_part));
7653     for ( i=0; i<cnt; ++i ) {
7654 	PyObject *obj = PySequence_Fast_GET_ITEM(seq, i);
7655 	int extender=0, start=0, end=0, full=0;
7656 	char *glyphName;
7657 	if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(obj)) ) {
7658 	    parts[i].component = copy( ((PyFF_Glyph *) obj)->sc->name );
7659 	} else if ( PyUnicode_Check(obj) ) {
7660 	    char* temp = copy(PyUnicode_AsUTF8(obj));
7661 	    if (temp == NULL) {
7662 	        FreeGVPartsList(parts, i);
7663 	        Py_DECREF(seq);
7664 	        return NULL;
7665 	    }
7666 	    parts[i].component = temp;
7667 	} else if ( PyTuple_Check(obj) && PyTuple_Size(obj)>0 &&
7668 		    PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(PyTuple_GetItem(obj,0))) ) {
7669 	    PyObject *g;
7670 	    if ( !PyArg_ParseTuple(obj,"O|iiii", &g, &extender, &start, &end, &full )) {
7671 		Py_DECREF(seq);
7672 		FreeGVPartsList(parts, i);
7673 return( NULL );
7674 	    }
7675 	    parts[i].component = copy(((PyFF_Glyph *) g)->sc->name);
7676 	} else if ( PyArg_ParseTuple(obj,"s|iiii", &glyphName,
7677 		&extender, &start, &end, &full )) {
7678 	    parts[i].component = copy(glyphName);
7679 	} else {
7680         Py_DECREF(seq);
7681         FreeGVPartsList(parts, i);
7682 return( NULL );
7683         }
7684 	parts[i].is_extender = extender;
7685 	parts[i].startConnectorLength = start;
7686 	parts[i].endConnectorLength = end;
7687 	parts[i].fullAdvance = full;
7688     }
7689     Py_DECREF(seq);
7690 return( parts );
7691 }
7692 
PyFF_Glyph_get_horizontalCIC(PyFF_Glyph * self,void * UNUSED (closure))7693 static PyObject *PyFF_Glyph_get_horizontalCIC(PyFF_Glyph *self, void *UNUSED(closure)) {
7694     if ( self->sc->horiz_variants==NULL )
7695 return( Py_BuildValue("i", 0));
7696 
7697 return( Py_BuildValue("i", self->sc->horiz_variants->italic_correction ));
7698 }
7699 
PyFF_Glyph_set_horizontalCIC(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7700 static int PyFF_Glyph_set_horizontalCIC(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7701     int val;
7702 
7703     val = PyLong_AsLong(value);
7704     if ( PyErr_Occurred()!=NULL )
7705 return( -1 );
7706     if ( self->sc->horiz_variants == NULL )
7707 	self->sc->horiz_variants = chunkalloc(sizeof(struct glyphvariants));
7708     self->sc->horiz_variants->italic_correction = val;
7709 return( 0 );
7710 }
7711 
PyFF_Glyph_get_verticalCIC(PyFF_Glyph * self,void * UNUSED (closure))7712 static PyObject *PyFF_Glyph_get_verticalCIC(PyFF_Glyph *self, void *UNUSED(closure)) {
7713     if ( self->sc->vert_variants==NULL )
7714 return( Py_BuildValue("i", 0));
7715 
7716 return( Py_BuildValue("i", self->sc->vert_variants->italic_correction ));
7717 }
7718 
PyFF_Glyph_set_verticalCIC(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7719 static int PyFF_Glyph_set_verticalCIC(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7720     int val;
7721 
7722     val = PyLong_AsLong(value);
7723     if ( PyErr_Occurred()!=NULL )
7724 return( -1 );
7725     if ( self->sc->vert_variants == NULL )
7726 	self->sc->vert_variants = chunkalloc(sizeof(struct glyphvariants));
7727     self->sc->vert_variants->italic_correction = val;
7728 return( 0 );
7729 }
7730 
PyFF_Glyph_get_verticalVariants(PyFF_Glyph * self,void * UNUSED (closure))7731 static PyObject *PyFF_Glyph_get_verticalVariants(PyFF_Glyph *self, void *UNUSED(closure)) {
7732     if ( self->sc->vert_variants==NULL || self->sc->vert_variants->variants==NULL )
7733 Py_RETURN_NONE;
7734 
7735 return( Py_BuildValue("s", self->sc->vert_variants->variants ));
7736 }
7737 
PyFF_Glyph_set_verticalVariants(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7738 static int PyFF_Glyph_set_verticalVariants(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7739     if ( value == Py_None ) {
7740 	if ( self->sc->vert_variants!=NULL ) {
7741 	    free(self->sc->vert_variants->variants);
7742 	    self->sc->vert_variants->variants=NULL;
7743 	}
7744     } else {
7745 	char *str = GlyphListToStr(value);
7746 	if ( str==NULL )
7747 return( -1 );
7748 	if ( self->sc->vert_variants == NULL )
7749 	    self->sc->vert_variants = chunkalloc(sizeof(struct glyphvariants));
7750 	self->sc->vert_variants->variants = str;
7751     }
7752 return( 0 );
7753 }
7754 
PyFF_Glyph_get_horizontalVariants(PyFF_Glyph * self,void * UNUSED (closure))7755 static PyObject *PyFF_Glyph_get_horizontalVariants(PyFF_Glyph *self, void *UNUSED(closure)) {
7756     if ( self->sc->horiz_variants==NULL || self->sc->horiz_variants->variants==NULL )
7757 Py_RETURN_NONE;
7758 
7759 return( Py_BuildValue("s", self->sc->horiz_variants->variants ));
7760 }
7761 
PyFF_Glyph_set_horizontalVariants(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7762 static int PyFF_Glyph_set_horizontalVariants(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7763     if ( value == Py_None ) {
7764 	if ( self->sc->horiz_variants!=NULL ) {
7765 	    free(self->sc->horiz_variants->variants);
7766 	    self->sc->horiz_variants->variants=NULL;
7767 	}
7768     } else {
7769 	char *str = GlyphListToStr(value);
7770 	if ( str==NULL )
7771 return( -1 );
7772 	if ( self->sc->horiz_variants == NULL )
7773 	    self->sc->horiz_variants = chunkalloc(sizeof(struct glyphvariants));
7774 	self->sc->horiz_variants->variants = str;
7775     }
7776 return( 0 );
7777 }
7778 
PyFF_Glyph_get_horizontalComponents(PyFF_Glyph * self,void * UNUSED (closure))7779 static PyObject *PyFF_Glyph_get_horizontalComponents(PyFF_Glyph *self, void *UNUSED(closure)) {
7780     if ( self->sc->horiz_variants==0 || self->sc->horiz_variants->part_cnt==0 )
7781 Py_RETURN_NONE;
7782 
7783 return( BuildComponentTuple(self->sc->horiz_variants ));
7784 }
7785 
PyFF_Glyph_set_horizontalComponents(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7786 static int PyFF_Glyph_set_horizontalComponents(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7787     int cnt;
7788     struct gv_part *parts;
7789 
7790     if ( value == Py_None ) {
7791 	if ( self->sc->horiz_variants!=NULL ) {
7792 	    FreeGVParts(self->sc->horiz_variants);
7793 	}
7794     } else {
7795 	parts = ParseComponentTuple(value,&cnt);
7796 	if ( parts==NULL )
7797 return( -1 );
7798 	FreeGVParts(self->sc->horiz_variants);
7799 	if ( self->sc->horiz_variants == NULL )
7800 	    self->sc->horiz_variants = chunkalloc(sizeof(struct glyphvariants));
7801 	self->sc->horiz_variants->part_cnt = cnt;
7802 	self->sc->horiz_variants->parts = parts;
7803     }
7804 return( 0 );
7805 }
7806 
PyFF_Glyph_get_verticalComponents(PyFF_Glyph * self,void * UNUSED (closure))7807 static PyObject *PyFF_Glyph_get_verticalComponents(PyFF_Glyph *self, void *UNUSED(closure)) {
7808     if ( self->sc->vert_variants==0 || self->sc->vert_variants->part_cnt==0 )
7809 Py_RETURN_NONE;
7810 
7811 return( BuildComponentTuple(self->sc->vert_variants ));
7812 }
7813 
PyFF_Glyph_set_verticalComponents(PyFF_Glyph * self,PyObject * value,void * UNUSED (closure))7814 static int PyFF_Glyph_set_verticalComponents(PyFF_Glyph *self,PyObject *value, void *UNUSED(closure)) {
7815     int cnt;
7816     struct gv_part *parts;
7817 
7818     if ( value == Py_None ) {
7819 	if ( self->sc->vert_variants!=NULL ) {
7820 	    FreeGVParts(self->sc->vert_variants);
7821 	}
7822     } else {
7823 	parts = ParseComponentTuple(value,&cnt);
7824 	if ( parts==NULL )
7825 return( -1 );
7826 	FreeGVParts(self->sc->vert_variants);
7827 	if ( self->sc->vert_variants == NULL )
7828 	    self->sc->vert_variants = chunkalloc(sizeof(struct glyphvariants));
7829 	self->sc->vert_variants->part_cnt = cnt;
7830 	self->sc->vert_variants->parts = parts;
7831     }
7832 return( 0 );
7833 }
7834 
PyFF_Glyph_get_mathKern(PyFF_Glyph * self,void * UNUSED (closure))7835 static PyObject *PyFF_Glyph_get_mathKern(PyFF_Glyph *self, void *UNUSED(closure)) {
7836     PyFF_MathKern *mk;
7837 
7838     if ( self->mk!=NULL )
7839 Py_RETURN( self->mk );
7840     mk = (PyFF_MathKern *) PyObject_New(PyFF_MathKern, &PyFF_MathKernType);
7841     if (mk == NULL)
7842 return NULL;
7843     mk->sc = self->sc;
7844     self->mk = mk;
7845 Py_RETURN( self->mk );
7846 }
7847 
7848 static PyGetSetDef PyFF_Glyph_getset[] = {
7849     {(char *)"userdata",
7850      (getter)PyFF_Glyph_get_temporary, (setter)PyFF_Glyph_set_temporary,
7851      (char *)"arbitrary (non persistent) user data (deprecated name for temporary)", NULL},
7852     {(char *)"temporary",
7853      (getter)PyFF_Glyph_get_temporary, (setter)PyFF_Glyph_set_temporary,
7854      (char *)"arbitrary (non persistent) user data", NULL},
7855     {(char *)"persistant",		/* I documented this member with the wrong spelling... so support it */
7856      (getter)PyFF_Glyph_get_persistent, (setter)PyFF_Glyph_set_persistent,
7857      (char *)"arbitrary persistent user data", NULL},
7858     {(char *)"persistent",
7859      (getter)PyFF_Glyph_get_persistent, (setter)PyFF_Glyph_set_persistent,
7860      (char *)"arbitrary persistent user data", NULL},
7861     {(char *)"activeLayer",
7862      (getter)PyFF_Glyph_get_activeLayer, (setter)PyFF_Glyph_set_activeLayer,
7863      (char *)"The layer in the glyph which is currently active", NULL},
7864     {(char *)"anchorPoints",
7865      (getter)PyFF_Glyph_get_anchorPoints, (setter)PyFF_Glyph_set_anchorPoints,
7866      (char *)"a tuple of all anchor points in the glyph", NULL},
7867 /* There is no set_anchorPointsWithSel because we don't need it. We set the selection if we find it */
7868     {(char *)"anchorPointsWithSel",
7869      (getter)PyFF_Glyph_get_anchorPointsWithSel, (setter)PyFF_Glyph_set_anchorPoints,
7870      (char *)"a tuple of all anchor points in the glyph (with selection indication)", NULL},
7871     {(char *)"glyphname",
7872      (getter)PyFF_Glyph_get_glyphname, (setter)PyFF_Glyph_set_glyphname,
7873      (char *)"glyph name", NULL},
7874     {(char *)"codepoint",
7875      (getter)PyFF_Glyph_get_codepoint, NULL,
7876      (char *)"Unicode code point for this glyph in U+XXXX format, or None (readonly)", NULL},
7877     {(char *)"unicode",
7878      (getter)PyFF_Glyph_get_unicode, (setter)PyFF_Glyph_set_unicode,
7879      (char *)"Unicode code point for this glyph, or -1", NULL},
7880     {(char *)"altuni",
7881      (getter)PyFF_Glyph_get_altuni, (setter)PyFF_Glyph_set_altuni,
7882      (char *)"Alternate unicode encodings (and variation selectors) for this glyph", NULL},
7883     {(char *)"encoding",
7884      (getter)PyFF_Glyph_get_encoding, NULL,
7885      (char *)"Returns the glyph's encoding in the current font (readonly)", NULL},
7886     {(char *)"foreground",
7887      (getter)PyFF_Glyph_get_a_layer, (setter)PyFF_Glyph_set_a_layer,
7888      (char *)"Returns the foreground layer of the glyph", (void *)ly_fore},
7889     {(char *)"background",
7890      (getter)PyFF_Glyph_get_a_layer, (setter)PyFF_Glyph_set_a_layer,
7891      (char *)"Returns the background layer of the glyph", (void *)ly_back},
7892     {(char *)"layers",
7893      (getter)PyFF_Glyph_get_layers, NULL,
7894      (char *)"Returns an array of layers", NULL},
7895     {(char *)"references",
7896      (getter)PyFF_Glyph_get_references, (setter)PyFF_Glyph_set_references,
7897      (char *)"A tuple of all references in the glyph", NULL},
7898     {(char *)"layerrefs",
7899      (getter)PyFF_Glyph_get_layerrefs, NULL,
7900      (char *)"Returns an array of layer references", NULL},
7901     {(char *)"layer_cnt",
7902      (getter)PyFF_Glyph_get_layer_cnt, NULL,
7903      (char *)"Returns the number of layers in the glyph", NULL},
7904     {(char *)"color",
7905      (getter)PyFF_Glyph_get_color, (setter)PyFF_Glyph_set_color,
7906      (char *)"Glyph color", NULL},
7907     {(char *)"comment",
7908      (getter)PyFF_Glyph_get_comment, (setter)PyFF_Glyph_set_comment,
7909      (char *)"Glyph comment", NULL},
7910     {(char *)"user_decomp",
7911      (getter)PyFF_Glyph_get_user_decomp, (setter)PyFF_Glyph_set_user_decomp,
7912      (char *)"Glyph user decompositon", NULL},
7913     {(char *)"glyphclass",
7914      (getter)PyFF_Glyph_get_glyphclass, (setter)PyFF_Glyph_set_glyphclass,
7915      (char *)"glyph class", NULL},
7916     {(char *)"italicCorrection",
7917      (getter)PyFF_Glyph_get_italiccorrection, (setter)PyFF_Glyph_set_italiccorrection,
7918      (char *)"Math & TeX italic correction", NULL},
7919     {(char *)"isExtendedShape",
7920      (getter)PyFF_Glyph_get_isextendedshape, (setter)PyFF_Glyph_set_isextendedshape,
7921      (char *)"Math \"is extended shape\" field", NULL},
7922     {(char *)"script",
7923      (getter)PyFF_Glyph_get_script, NULL,
7924      (char *)"The OpenType script containing this glyph (readonly)", NULL},
7925     {(char *)"texheight",
7926      (getter)PyFF_Glyph_get_texheight, (setter)PyFF_Glyph_set_texheight,
7927      (char *)"TeX glyph height", NULL},
7928     {(char *)"texdepth",
7929      (getter)PyFF_Glyph_get_texdepth, (setter)PyFF_Glyph_set_texdepth,
7930      (char *)"TeX glyph depth", NULL},
7931     {(char *)"topaccent",
7932      (getter)PyFF_Glyph_get_topaccent, (setter)PyFF_Glyph_set_topaccent,
7933      (char *)"Math top accent horizontal position", NULL},
7934     {(char *)"ttinstrs",
7935      (getter)PyFF_Glyph_get_ttfinstrs, (setter)PyFF_Glyph_set_ttfinstrs,
7936      (char *)"TrueType Instructions for this glyph", NULL},
7937     {(char *)"unlinkRmOvrlpSave",
7938      (getter)PyFF_Glyph_get_unlinkRmOvrlpSave, (setter)PyFF_Glyph_set_unlinkRmOvrlpSave,
7939      (char *)"A flag which indicates that before the glyph is saved ff should unlink its references and run remove overlap on it.", NULL},
7940     {(char *)"changed",
7941      (getter)PyFF_Glyph_get_changed, (setter)PyFF_Glyph_set_changed,
7942      (char *)"Flag indicating whether this glyph has changed", NULL},
7943     {(char *)"originalgid",
7944      (getter)PyFF_Glyph_get_originalgid, NULL,
7945      (char *)"Original GID (readonly)", NULL},
7946     {(char *)"width",
7947      (getter)PyFF_Glyph_get_width, (setter)PyFF_Glyph_set_width,
7948      (char *)"Glyph's advance width", NULL},
7949     {(char *)"left_side_bearing",
7950      (getter)PyFF_Glyph_get_lsb, (setter)PyFF_Glyph_set_lsb,
7951      (char *)"Glyph's left side bearing", NULL},
7952     {(char *)"right_side_bearing",
7953      (getter)PyFF_Glyph_get_rsb, (setter)PyFF_Glyph_set_rsb,
7954      (char *)"Glyph's right side bearing", NULL},
7955     {(char *)"vwidth",
7956      (getter)PyFF_Glyph_get_vwidth, (setter)PyFF_Glyph_set_vwidth,
7957      (char *)"Glyph's vertical advance width", NULL},
7958     {(char *)"font",
7959      (getter)PyFF_Glyph_get_font, NULL,
7960      (char *)"Font containing the glyph (readonly)", NULL},
7961     {(char *)"hhints",
7962      (getter)PyFF_Glyph_get_hhints, (setter)PyFF_Glyph_set_hhints,
7963      (char *)"The horizontal hints of the glyph as a tuple, one entry per hint. Each hint is itself a tuple containing the start location and width of the hint", NULL},
7964     {(char *)"vhints",
7965      (getter)PyFF_Glyph_get_vhints, (setter)PyFF_Glyph_set_vhints,
7966      (char *)"The vertical hints of the glyph as a tuple, one entry per hint. Each hint is itself a tuple containing the start location and width of the hint", NULL},
7967     {(char *)"dhints",
7968      (getter)PyFF_Glyph_get_dhints, (setter)PyFF_Glyph_set_dhints,
7969      (char *)"The diagonal hints of the glyph as a tuple, one entry per hint. Each hint is itself a tuple containing three pairs of coordinates, specifying a point on the left edge, a point on the right edge and a unit vector for this hint.", NULL},
7970     {(char *)"manualHints",
7971      (getter)PyFF_Glyph_get_manualhints, (setter)PyFF_Glyph_set_manualhints,
7972      (char *)"The hints have been set manually, and the glyph should not be autohinted by default", NULL },
7973     {(char *)"lcarets",
7974      (getter)PyFF_Glyph_get_lcarets, (setter)PyFF_Glyph_set_lcarets,
7975      (char *)"The ligature caret locations, defined for this glyph, as a tuple.", NULL},
7976     {(char *)"validation_state",
7977      (getter)PyFF_Glyph_get_validation_state, NULL,
7978      (char *)"glyph's validation state (readonly)", NULL},
7979     {(char *)"horizontalVariants",
7980      (getter)PyFF_Glyph_get_horizontalVariants, (setter)PyFF_Glyph_set_horizontalVariants,
7981      (char *)"glyph's horizontal variants (for math typesetting) as a string of glyph names", NULL},
7982     {(char *)"verticalVariants",
7983      (getter)PyFF_Glyph_get_verticalVariants, (setter)PyFF_Glyph_set_verticalVariants,
7984      (char *)"glyph's vertical variants (for math typesetting) as a string of glyph names", NULL},
7985     {(char *)"horizontalComponents",
7986      (getter)PyFF_Glyph_get_horizontalComponents, (setter)PyFF_Glyph_set_horizontalComponents,
7987      (char *)"A way of build very large versions of the glyph (for math typesetting) out of lots of smaller glyphs.", NULL},
7988     {(char *)"verticalComponents",
7989      (getter)PyFF_Glyph_get_verticalComponents, (setter)PyFF_Glyph_set_verticalComponents,
7990      (char *)"A way of build very large versions of the glyph (for math typesetting) out of lots of smaller glyphs.", NULL},
7991     {(char *)"horizontalComponentItalicCorrection",
7992      (getter)PyFF_Glyph_get_horizontalCIC, (setter)PyFF_Glyph_set_horizontalCIC,
7993      (char *)"The italic correction for any composite glyph made with the horizontalComponents.", NULL},
7994     {(char *)"verticalComponentItalicCorrection",
7995      (getter)PyFF_Glyph_get_verticalCIC, (setter)PyFF_Glyph_set_verticalCIC,
7996      (char *)"The italic correction for any composite glyph made with the verticalComponents.", NULL},
7997     {(char *)"mathKern",
7998      (getter)PyFF_Glyph_get_mathKern, NULL,
7999      (char *)"math kerning information for the glyph.", NULL},
8000     PYGETSETDEF_EMPTY  /* Sentinel */
8001 };
8002 
8003 /* ************************************************************************** */
8004 /*  Glyph Methods  */
8005 /* ************************************************************************** */
8006 
PyFFGlyph_Build(PyObject * self,PyObject * args)8007 static PyObject *PyFFGlyph_Build(PyObject *self, PyObject *args) {
8008     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8009     int layer = ((PyFF_Glyph *) self)->layer;
8010     int accent_hint = false;
8011     PyObject *accent_hint_pyo = NULL;
8012 
8013     if ( PyArg_ParseTuple(args, "|O", &accent_hint_pyo) ) {
8014         if (accent_hint_pyo == Py_True) {
8015             accent_hint = true;
8016         }
8017     }
8018 
8019     if ( SFIsSomethingBuildable(sc->parent,sc,layer,false) )
8020 	SCBuildComposit(sc->parent,sc,layer,NULL,true,accent_hint);
8021 
8022 Py_RETURN( self );
8023 }
8024 
8025 static const char *appendaccent_keywords[] = { "name", "unicode", "pos", NULL };
8026 
PyFFGlyph_appendAccent(PyObject * self,PyObject * args,PyObject * keywds)8027 static PyObject *PyFFGlyph_appendAccent(PyObject *self, PyObject *args, PyObject *keywds) {
8028     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8029     int layer = ((PyFF_Glyph *) self)->layer;
8030     int pos = FF_UNICODE_NOPOSDATAGIVEN; /* unicode char pos info, see #define for (uint32)(utype2[]) */
8031     int uni=-1;				/* unicode char value */
8032     char *name = NULL;			/* unicode char name */
8033     int ret;
8034 
8035     if ( !PyArg_ParseTupleAndKeywords(args,keywds,"|sii",(char **)appendaccent_keywords,
8036 	    &name, &uni, &pos))
8037 return( NULL );
8038     if ( name==NULL && uni==-1 ) {
8039 	PyErr_Format(PyExc_ValueError, "You must specify either a name of a unicode code point");
8040 return( NULL );
8041     }
8042     ret = SCAppendAccent(sc,layer,name,uni,(uint32)(pos));
8043     if ( ret==1 ) {
8044 	PyErr_Format(PyExc_ValueError, "No base character reference found");
8045 return( NULL );
8046     } else if ( ret==2 ) {
8047 	PyErr_Format(PyExc_ValueError, "Could not find that accent");
8048 return( NULL );
8049     }
8050     SCCharChangedUpdate(sc,layer);
8051 
8052 Py_RETURN( self );
8053 }
8054 
PyFFGlyph_useRefsMetrics(PyObject * self,PyObject * args)8055 static PyObject *PyFFGlyph_useRefsMetrics(PyObject *self, PyObject *args) {
8056     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8057     int layer = ((PyFF_Glyph *) self)->layer;
8058     RefChar *ref, *itwilldo;
8059     char *name=NULL;
8060     int setting=true;
8061 
8062     if ( !PyArg_ParseTuple(args,"s|i", &name, &setting ))
8063 return( NULL );
8064     for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next )
8065 	ref->use_my_metrics = 0;
8066     itwilldo = NULL;
8067     for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next )
8068 	if ( strcmp(ref->sc->name,name)==0 ) {
8069 	    if ( ref->transform[0]==1 && ref->transform[3]==1 &&
8070 		    ref->transform[1]==0 && ref->transform[2]==0 &&
8071 		    ref->transform[4]==0 && ref->transform[5]==0 )
8072     break;
8073 	    else
8074 		itwilldo = ref;
8075 	}
8076     if ( ref==NULL )
8077 	ref = itwilldo;
8078     if ( ref==NULL ) {
8079 	PyErr_Format(PyExc_ValueError, "Could not find a reference named %s", name );
8080 return( NULL );
8081     }
8082     if ( setting ) {
8083 	ref->use_my_metrics = true;
8084 	sc->width = ref->sc->width;
8085     }
8086 
8087 Py_RETURN( self );
8088 }
8089 
PyFFGlyph_canonicalContours(PyObject * self,PyObject * UNUSED (args))8090 static PyObject *PyFFGlyph_canonicalContours(PyObject *self, PyObject *UNUSED(args)) {
8091     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8092 
8093     CanonicalContours(sc,((PyFF_Glyph *) self)->layer);
8094 
8095 Py_RETURN( self );
8096 }
8097 
PyFFGlyph_canonicalStart(PyObject * self,PyObject * UNUSED (args))8098 static PyObject *PyFFGlyph_canonicalStart(PyObject *self, PyObject *UNUSED(args)) {
8099     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8100 
8101     SPLsStartToLeftmost(sc,((PyFF_Glyph *) self)->layer);
8102 
8103 Py_RETURN( self );
8104 }
8105 
8106 /* Char weight/Embolden types: see 'enum embolden_type' in baseviews.h */
8107 static struct flaglist cw_types[] = {
8108     { "lcg", embolden_lcg },
8109     { "cjk", embolden_cjk },
8110     { "auto", embolden_auto },
8111     { "custom", embolden_custom },
8112     { "LCG", embolden_lcg },
8113     { "CJK", embolden_cjk },
8114     FLAGLIST_EMPTY /* Sentinel */
8115 };
8116 
8117 /* Counter types: see 'enum counter_type' in baseviews.h */
8118 static struct flaglist co_types[] = {
8119     { "squish", ct_squish },
8120     { "retain", ct_retain },
8121     { "auto", ct_auto },
8122     FLAGLIST_EMPTY /* Sentinel */
8123 };
8124 
CW_ParseArgs(SplineFont * sf,struct lcg_zones * zones,PyObject * args)8125 static enum embolden_type CW_ParseArgs(SplineFont *sf, struct lcg_zones *zones, PyObject *args) {
8126     enum embolden_type type;
8127     const char *type_name="auto", *counter_name = "auto";
8128     PyObject *zoneO=NULL;
8129     int just_top;
8130 
8131     memset(zones,0,sizeof(*zones));
8132     zones->serif_height = -1;
8133     zones->serif_fuzz = .9;
8134 
8135     if ( !PyArg_ParseTuple(args,"d|sddsiO",
8136 	    &zones->stroke_width, &type_name,
8137 	    &zones->serif_height, &zones->serif_fuzz,
8138 	    &counter_name,
8139 	    &zones->removeoverlap,
8140 	    &zoneO ))
8141 return( embolden_error );
8142     type = FlagsFromString(type_name,cw_types,"embolden type");
8143     if ( type==(uint32)FLAG_UNKNOWN )
8144 return( embolden_error );
8145     zones->counter_type = FlagsFromString(counter_name,co_types,"counter type");
8146     if ( zones->counter_type==(uint32)FLAG_UNKNOWN )
8147 return( embolden_error );
8148 
8149     just_top = true;
8150     if ( zoneO==NULL )
8151 	zones->top_bound = sf->ascent/2;
8152     else if ( PyLong_Check(zoneO))
8153 	zones->top_bound = PyLong_AsLong(zoneO);
8154     else if ( PyTuple_Check(zoneO)) {
8155 	if ( !PyArg_ParseTuple(zoneO,"dddd",
8156 		&zones->top_bound,&zones->top_zone,&zones->bottom_zone,&zones->bottom_bound))
8157 return( embolden_error );
8158 	just_top = false;
8159     }
8160     if ( just_top ) {
8161 	/* Bottom defaults to 0, and the other two are between */
8162 	zones->top_zone = 3*zones->top_bound/4;
8163 	zones->bottom_zone = zones->top_bound/4;
8164     }
8165 
8166 return( type );
8167 }
8168 
PyFFGlyph_changeWeight(PyObject * self,PyObject * args)8169 static PyObject *PyFFGlyph_changeWeight(PyObject *self, PyObject *args) {
8170     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8171     enum embolden_type type;
8172     struct lcg_zones zones;
8173 
8174     type = CW_ParseArgs(sc->parent,&zones,args);
8175     if ( type == embolden_error )
8176 return( NULL );
8177     ScriptSCEmbolden(sc,((PyFF_Glyph *) self)->layer,type,&zones);
8178 
8179 Py_RETURN( self );
8180 }
8181 
PyFFGlyph_condenseExtend(PyObject * self,PyObject * args)8182 static PyObject *PyFFGlyph_condenseExtend(PyObject *self, PyObject *args) {
8183     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8184     struct counterinfo ci;
8185 
8186     memset(&ci,0,sizeof(ci));
8187     ci.sb_factor = ci.sb_add = -10000;
8188     ci.correct_italic = true;
8189     ci.layer = ((PyFF_Glyph *) self)->layer;
8190 
8191     if ( !PyArg_ParseTuple(args,"dd|ddi",&ci.c_factor, &ci.c_add, &ci.sb_factor, &ci.sb_add,
8192 	    &ci.correct_italic ))
8193 return( NULL );
8194     ci.c_factor *= 100;			/* UI uses a percent */
8195     if ( ci.sb_factor == -10000 )
8196 	ci.sb_factor = ci.c_factor;
8197     if ( ci.sb_add == -10000 )
8198 	ci.sb_add = ci.c_add;
8199     CI_Init(&ci,sc->parent);
8200     ScriptSCCondenseExtend(sc,&ci);
8201 
8202 Py_RETURN( self );
8203 }
8204 
PyFFGlyph_AddReference(PyObject * self,PyObject * args)8205 static PyObject *PyFFGlyph_AddReference(PyObject *self, PyObject *args) {
8206     double m[6];
8207     real transform[6];
8208     char *str;
8209     SplineChar *sc = ((PyFF_Glyph *) self)->sc, *rsc;
8210     SplineFont *sf = sc->parent;
8211     int j;
8212 
8213     memset(m,0,sizeof(m));
8214     m[0] = m[3] = 1;
8215     if ( !PyArg_ParseTuple(args,"s|(dddddd)",&str,
8216 	    &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) )
8217 return( NULL );
8218     rsc = SFGetChar(sf,-1,str);
8219     if ( rsc==NULL ) {
8220 	PyErr_Format(PyExc_EnvironmentError, "No glyph named %s", str);
8221 return( NULL );
8222     }
8223     for ( j=0; j<6; ++j )
8224 	transform[j] = m[j];
8225     _SCAddRef(sc,rsc,((PyFF_Glyph *) self)->layer,transform);
8226     SCCharChangedUpdate(sc,((PyFF_Glyph *) self)->layer);
8227 
8228 Py_RETURN( self );
8229 }
8230 
PyFFGlyph_addAnchorPoint(PyObject * self,PyObject * args)8231 static PyObject *PyFFGlyph_addAnchorPoint(PyObject *self, PyObject *args) {
8232     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8233     AnchorPoint *ap = APFromTuple(sc,args), *ap2;
8234     int done = false;
8235 
8236     if ( ap==NULL )
8237 return( NULL );
8238 
8239     for ( ap2=sc->anchor; ap2!=NULL; ap2=ap2->next ) {
8240         switch ( ap2->anchor->type ) {
8241             default:
8242                 if ( ap2->anchor->name==ap->anchor->name && ap2->type==ap->type ) {
8243                     ap->next = ap2->next;
8244                     *ap2 = *ap;
8245                     done = true;
8246 		}
8247 		break;
8248             case act_mklg:
8249                 if ( ap2->anchor->name==ap->anchor->name && ap2->lig_index==ap->lig_index ) {
8250                     ap->next = ap2->next;
8251                     *ap2 = *ap;
8252                     done = true;
8253 		}
8254 		break;
8255             case act_mark:
8256                 if ( ap2->anchor->name==ap->anchor->name ) {
8257                     ap->next = ap2->next;
8258                     *ap2 = *ap;
8259                     done = true;
8260 		}
8261 		break;
8262 	}
8263     }
8264 
8265     if ( !done ) {
8266        ap->next = sc->anchor;
8267        sc->anchor = ap;
8268     }
8269 
8270     SCCharChangedUpdate(sc,((PyFF_Glyph *) self)->layer);
8271 
8272 Py_RETURN( self );
8273 }
8274 
8275 static const char *glyphpen_keywords[] = { "replace", NULL };
PyFFGlyph_GlyphPen(PyObject * self,PyObject * args,PyObject * keywds)8276 static PyObject *PyFFGlyph_GlyphPen(PyObject *self, PyObject *args, PyObject *keywds) {
8277     int replace = true;
8278     PyObject *gp;
8279 
8280     if ( !PyArg_ParseTupleAndKeywords(args, keywds, "|i", (char **)glyphpen_keywords,
8281 	    &replace ))
8282 return( NULL );
8283     gp = PyFF_GlyphPenType.tp_alloc(&PyFF_GlyphPenType,0);
8284     ((PyFF_GlyphPen *) gp)->sc = ((PyFF_Glyph *) self)->sc;
8285     ((PyFF_GlyphPen *) gp)->layer = ((PyFF_Glyph *) self)->layer;
8286     ((PyFF_GlyphPen *) gp)->replace = replace;
8287     ((PyFF_GlyphPen *) gp)->ended = true;
8288     ((PyFF_GlyphPen *) gp)->changed = false;
8289     /* tp_alloc increments the reference count for us */
8290 return( gp );
8291 }
8292 
PyFFGlyph_draw(PyObject * self,PyObject * args)8293 static PyObject *PyFFGlyph_draw(PyObject *self, PyObject *args) {
8294     PyObject *layer, *result, *pen;
8295     RefChar *ref;
8296     PyObject *tuple;
8297 
8298     if ( !PyArg_ParseTuple(args,"O", &pen ) )
8299 return( NULL );
8300 
8301     layer = PyFF_Glyph_get_a_layer((PyFF_Glyph *) self,(void *)(size_t)((PyFF_Glyph *) self)->layer);
8302     result = PyFFLayer_draw( (PyFF_Layer *) layer,args);
8303     Py_XDECREF(layer);
8304 
8305     for ( ref = ((PyFF_Glyph *) self)->sc->layers[((PyFF_Glyph *) self)->layer].refs; ref!=NULL; ref=ref->next ) {
8306 	tuple = Py_BuildValue("s(dddddd)", ref->sc->name,
8307 		ref->transform[0], ref->transform[1], ref->transform[2],
8308 		ref->transform[3], ref->transform[4], ref->transform[5]);
8309 	do_pycall(pen,"addComponent",tuple);
8310     }
8311 return( result );
8312 }
8313 
8314 static bool PyFFParse_genericGlyphChange(PyObject *args, PyObject *keywds,
8315                                          struct genericchange *genchange);
8316 
PyFFGlyph_genericGlyphChange(PyFF_Glyph * self,PyObject * args,PyObject * keywds)8317 static PyObject *PyFFGlyph_genericGlyphChange(PyFF_Glyph *self, PyObject *args,
8318                                               PyObject *keywds) {
8319     SplineChar *sc = self->sc;
8320     struct genericchange genchange;
8321     struct smallcaps small;
8322 
8323     if ( self->layer < 0 )
8324 	Py_RETURN( self );
8325 
8326     if ( !PyFFParse_genericGlyphChange(args, keywds, &genchange) )
8327 	return NULL;
8328 
8329     SmallCapsFindConstants(&small,sc->parent,self->layer);
8330     genchange.small = &small;
8331     genchange.italic_angle = small.italic_angle;
8332     genchange.tan_ia = small.tan_ia;
8333 
8334     genchange.g.cnt = genchange.m.cnt+2;
8335     genchange.g.maps = malloc(genchange.g.cnt*sizeof(struct position_maps));
8336 
8337     if ( sc->layers[self->layer].splines!=NULL )
8338 	ChangeGlyph(sc,sc,self->layer,&genchange);
8339 
8340     free(genchange.g.maps);
8341     free(genchange.m.maps);
8342 
8343     Py_RETURN( self );
8344 }
8345 
PyFFGlyph_addHint(PyObject * self,PyObject * args)8346 static PyObject *PyFFGlyph_addHint(PyObject *self, PyObject *args) {
8347     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8348     int layer = ((PyFF_Glyph *) self)->layer;
8349     int is_v;
8350     double start, width;
8351     StemInfo *h;
8352 
8353     if ( !PyArg_ParseTuple(args,"idd", &is_v, &start, &width ) )
8354 return( NULL );
8355 
8356     h = chunkalloc(sizeof(StemInfo));
8357     if ( width==-20 || width==-21 )
8358 	h->ghost = true;
8359     if ( width<0 ) {
8360 	start += width;
8361 	width = -width;
8362     }
8363     h->start = start;
8364     h->width = width;
8365     if ( is_v ) {
8366 	SCGuessVHintInstancesAndAdd(sc,layer,h,0x80000000,0x80000000);
8367 	h->next = sc->vstem;
8368 	sc->vstem = HintCleanup(h,true,1);
8369 	sc->vconflicts = StemListAnyConflicts(sc->vstem);
8370     } else {
8371 	SCGuessHHintInstancesAndAdd(sc,layer,h,0x80000000,0x80000000);
8372 	h->next = sc->hstem;
8373 	sc->hstem = HintCleanup(h,true,1);
8374 	sc->hconflicts = StemListAnyConflicts(sc->hstem);
8375     }
8376 Py_RETURN( self );
8377 }
8378 
PyFFGlyph_autoHint(PyObject * self,PyObject * UNUSED (args))8379 static PyObject *PyFFGlyph_autoHint(PyObject *self, PyObject *UNUSED(args)) {
8380     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8381     int layer = ((PyFF_Glyph *) self)->layer;
8382 
8383     SplineCharAutoHint(sc,layer,NULL);
8384     SCUpdateAll(sc);
8385 Py_RETURN( self );
8386 }
8387 
PyFFGlyph_autoInstr(PyObject * self,PyObject * UNUSED (args))8388 static PyObject *PyFFGlyph_autoInstr(PyObject *self, PyObject *UNUSED(args)) {
8389     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8390 
8391     GlobalInstrCt gic;
8392     InitGlobalInstrCt(&gic,sc->parent,((PyFF_Glyph *) self)->layer,NULL);
8393     NowakowskiSCAutoInstr(&gic,sc);
8394     FreeGlobalInstrCt(&gic);
8395 Py_RETURN( self );
8396 }
8397 
PyFFGlyph_autoTrace(PyObject * self,PyObject * UNUSED (args))8398 static PyObject *PyFFGlyph_autoTrace(PyObject *self, PyObject *UNUSED(args)) {
8399     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8400     int layer = ((PyFF_Glyph *) self)->layer;
8401     char **at_args;
8402 
8403     at_args = AutoTraceArgs(false);
8404     if ( at_args==(char **) -1 ) {
8405 	PyErr_Format(PyExc_EnvironmentError, "Bad autotrace args" );
8406 return(NULL);
8407     }
8408     _SCAutoTrace(sc, layer, at_args);
8409 Py_RETURN( self );
8410 }
8411 
8412 static char *glyph_import_keywords[] = { "filename", "correctdir",
8413     "simplify", "handle_clip", "handle_eraser", "scale", "accuracy",
8414     "default_joinlimit", "usesystem", "asksystem", NULL };
8415 
8416 /* Legacy PostScript importing flags */
8417 static struct flaglist import_ps_flags[] = {
8418     { "toobigwarn", 0 },			/* Obsolete */
8419     { "removeoverlap", 0 },			/* Obsolete */
8420     { "handle_eraser", 1 },
8421     { "correctdir",  2 },
8422     FLAGLIST_EMPTY /* Sentinel */
8423 };
8424 
PyFFGlyph_import(PyObject * self,PyObject * args,PyObject * keywds)8425 static PyObject *PyFFGlyph_import(PyObject *self, PyObject *args,
8426                                   PyObject *keywds) {
8427     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8428     ImportParams ip, *ipp;
8429     char *filename;
8430     char *locfilename = NULL, *pt;
8431     PyObject *flags=NULL;
8432     int psflags, use_system = false, ask_system = false;
8433     bigreal jl_tmp = -1;
8434 
8435     InitImportParams(&ip);
8436 
8437     if ( !PyArg_ParseTupleAndKeywords(args, keywds,
8438                 "s|$pppppddpp", glyph_import_keywords, &filename,
8439                 &ip.correct_direction, &ip.simplify, &ip.clip, &ip.erasers,
8440                 &ip.scale, &ip.accuracy_target, &jl_tmp, &use_system,
8441 		&ask_system) ) {
8442 	PyErr_Clear();
8443 	if ( !PyArg_ParseTuple(args,"s|O",&filename, &flags) )
8444 	    return NULL;
8445 	psflags = FlagsFromTuple(flags, import_ps_flags,
8446 	                         "PostScript import flag");
8447 	if ( psflags==FLAG_UNKNOWN )
8448 	    return NULL;
8449 	if ( psflags & 1 )
8450 	    ip.erasers = true;
8451 	if ( psflags & 2 )
8452 	    ip.correct_direction = true;
8453     }
8454     locfilename = utf82def_copy(filename);
8455 
8456     /* Check if the file exists and is readable */
8457     if ( access(locfilename,R_OK)!=0 ) {
8458 	PyErr_SetFromErrnoWithFilename(PyExc_IOError,locfilename);
8459 	free(locfilename);
8460 	return NULL;
8461     }
8462 
8463     if ( use_system || ask_system ) {
8464 	ipp = ImportParamsState();
8465 	if ( ask_system )
8466 	    ImportParamsDlg(ipp);
8467     } else
8468 	ipp = &ip;
8469 
8470     pt = strrchr(locfilename,'.');
8471     if ( pt==NULL ) pt=locfilename;
8472 
8473     if ( strcasecmp(pt,".eps")==0 || strcasecmp(pt,".ps")==0 || strcasecmp(pt,".art")==0 ) {
8474 	SCImportPS(sc,((PyFF_Glyph *) self)->layer,locfilename,false,ipp);
8475     }
8476     else if ( strcasecmp(pt,".svg")==0 ) {
8477 	SCImportSVG(sc,((PyFF_Glyph *) self)->layer,locfilename,NULL,0,false,ipp);
8478     }
8479     else if ( strcasecmp(pt,".glif")==0 ) {
8480 	SCImportGlif(sc,((PyFF_Glyph *) self)->layer,locfilename,NULL,0,false,ipp);
8481     }
8482     else if ( strcasecmp(pt,".plate")==0 ) {
8483 	FILE *plate = fopen(locfilename,"r");
8484 	if ( plate==NULL ) {
8485 	    PyErr_SetFromErrnoWithFilename(PyExc_IOError,locfilename);
8486 	    free(locfilename);
8487 return( NULL );
8488 	}
8489 	else {
8490 	    SCImportPlateFile(sc,((PyFF_Glyph *) self)->layer,plate,false,ipp);
8491 	    fclose(plate);
8492 	}
8493     } /* else if ( strcasecmp(pt,".fig")==0 )*/
8494     else {
8495 	GImage *image = GImageRead(locfilename);
8496 	int ly = ((PyFF_Glyph *) self)->layer;
8497 	if ( image==NULL ) {
8498 	    PyErr_Format(PyExc_EnvironmentError, "Could not load image file \"%s\"", locfilename );
8499 	    free(locfilename);
8500 return(NULL);
8501 	}
8502 	if ( !sc->layers[ly].background )
8503 	    ly = ly_back;
8504 	SCAddScaleImage(sc,image,false,ly,ipp);
8505     }
8506     free( locfilename );
8507 Py_RETURN( self );
8508 }
8509 
8510 static char *glyph_export_keywords[] = { "filename", "layer", "pixelsize",
8511     "bitdepth", "usetransform", "usesystem", "asksystem", NULL };
8512 
PyFFGlyph_export(PyObject * self,PyObject * args,PyObject * keywds)8513 static PyObject *PyFFGlyph_export(PyObject *self, PyObject *args,
8514                                   PyObject *keywds) {
8515     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8516     char *filename;
8517     char *locfilename = NULL;
8518     char *pt;
8519     int pixels=100, bits=8;
8520     int format=-1, use_system=false, ask_system=false;
8521     FILE *file;
8522     int layer = ((PyFF_Glyph *) self)->layer;
8523     PyObject *layerobj = NULL, *obj1=NULL, *obj2=NULL;
8524     ExportParams ep, *epp;
8525 
8526     InitExportParams(&ep);
8527 
8528     if ( !PyArg_ParseTupleAndKeywords(args, keywds, "s|$Oiippp",
8529                                       glyph_export_keywords, &filename,
8530                                       &layerobj, &pixels, &bits,
8531                                       &ep.use_transform, &use_system,
8532                                       &ask_system) ) {
8533 	PyErr_Clear();
8534 	if ( !PyArg_ParseTuple(args,"s|OO",&filename,&obj1,&obj2) )
8535 	    return NULL;
8536     }
8537     locfilename = utf82def_copy(filename);
8538 
8539     if ( use_system || ask_system ) {
8540 	epp = ExportParamsState();
8541 	if ( ask_system )
8542 	    ExportParamsDlg(epp);
8543     } else
8544 	epp = &ep;
8545 
8546     pt = strrchr(locfilename,'.');
8547     if ( pt==NULL ) pt=locfilename;
8548     if ( strcasecmp(pt,".xbm")==0 ) {
8549 	format=0; bits=1;
8550     } else if ( strcasecmp(pt,".bmp")==0 )
8551 	format=1;
8552     else if ( strcasecmp(pt,".png")==0 )
8553 	format=2;
8554 
8555     if ( format!=-1 ) {
8556 	if ( obj1!=NULL ) {
8557 	    if ( PyLong_Check(obj1) ) {
8558 		pixels = PyLong_AsLong(obj1);
8559 	    } else {
8560 		free(locfilename);
8561 		return NULL;
8562 	    }
8563 	}
8564 	if ( obj2!=NULL ) {
8565 	    if ( PyLong_Check(obj2) ) {
8566 		bits = PyLong_AsLong(obj2);
8567 	    } else {
8568 		free(locfilename);
8569 		return NULL;
8570 	    }
8571 	}
8572 	if ( !ExportImage(locfilename,sc,layer,format,pixels,bits)) {
8573 	    PyErr_Format(PyExc_EnvironmentError, "Could not create image file \"%s\"", locfilename );
8574 	    free(locfilename);
8575 return( NULL );
8576 	}
8577     } else {
8578 	file = fopen( locfilename,"wb");
8579 	if ( file==NULL ) {
8580 	    PyErr_SetFromErrnoWithFilename(PyExc_IOError,locfilename);
8581 	    free(locfilename);
8582 return( NULL );
8583 	}
8584 
8585 	if ( obj1!=NULL ) {
8586 	    layer = LayerArgToLayer(sc->parent, obj1);
8587 	    if ( layer==ly_none ) {
8588 		free(locfilename);
8589 		fclose(file);
8590 		return NULL;
8591 	    }
8592 	}
8593 	if ( layer<0 || layer>=sc->layer_cnt ) {
8594 	    PyErr_Format(PyExc_ValueError, "Layer is out of range" );
8595 	    free(locfilename);
8596 	    fclose(file);
8597 return( NULL );
8598 	}
8599 
8600 	if ( strcasecmp(pt,".eps")==0 || strcasecmp(pt,".ps")==0 || strcasecmp(pt,".art")==0 )
8601 	    _ExportEPS(file,sc,layer,true);
8602 	else if ( strcasecmp(pt,".pdf")==0 )
8603 	    _ExportPDF(file,sc,layer);
8604 	else if ( strcasecmp(pt,".svg")==0 )
8605 	    _ExportSVG(file,sc,layer,epp);
8606 	else if ( strcasecmp(pt,".glif")==0 )
8607 	    _ExportGlif(file,sc,layer,3);
8608 	else if ( strcasecmp(pt,".glif2")==0 )
8609 	    _ExportGlif(file,sc,layer,2);
8610 	else if ( strcasecmp(pt,".glif3")==0 )
8611 	    _ExportGlif(file,sc,layer,3);
8612 	else if ( strcasecmp(pt,".plate")==0 )
8613 	    _ExportPlate(file,sc,layer);
8614 	/* else if ( strcasecmp(pt,".fig")==0 )*/
8615 	else {
8616 	    PyErr_Format(PyExc_TypeError, "Unknown extension to export: %s", pt );
8617 	    free( locfilename );
8618 	    fclose(file);
8619 return( NULL );
8620 	}
8621 	fclose(file);
8622     }
8623     free( locfilename );
8624 Py_RETURN( self );
8625 }
8626 
PyFFGlyph_unlinkRef(PyObject * self,PyObject * args)8627 static PyObject *PyFFGlyph_unlinkRef(PyObject *self, PyObject *args) {
8628     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8629     int layer = ((PyFF_Glyph *) self)->layer;
8630     char *refname=NULL;
8631     RefChar *ref;
8632     int any = false;
8633 
8634     if ( !PyArg_ParseTuple(args,"|s",&refname) )
8635 return( NULL );
8636     for ( ref = sc->layers[layer].refs; ref!=NULL; ref=ref->next ) {
8637 	if ( refname==NULL || strcmp(ref->sc->name,refname)==0 ) {
8638 	    SCRefToSplines(sc,ref,layer);
8639 	    any = true;
8640 	}
8641     }
8642 
8643     if ( !any && refname!=NULL ) {
8644 	PyErr_Format(PyExc_EnvironmentError, "No reference named %s found in glyph %s", refname, sc->name );
8645 return( NULL );
8646     }
8647     if ( any )
8648 	SCCharChangedUpdate(sc,((PyFF_Glyph *) self)->layer);
8649 Py_RETURN( self );
8650 }
8651 
PyFFGlyph_unlinkThisGlyph(PyObject * self,PyObject * UNUSED (args))8652 static PyObject *PyFFGlyph_unlinkThisGlyph(PyObject *self, PyObject *UNUSED(args)) {
8653     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8654     int layer = ((PyFF_Glyph *) self)->layer;
8655 
8656     UnlinkThisReference(NULL,sc,layer);
8657 Py_RETURN( self );
8658 }
8659 
TupleOfGlyphNames(char * str,int extras)8660 static PyObject *TupleOfGlyphNames(char *str,int extras) {
8661     int cnt;
8662     char *pt, *start;
8663     PyObject *tuple;
8664     int ch;
8665 
8666     for ( pt=str; *pt==' '; ++pt );
8667     if ( *pt=='\0' )
8668 return( PyTuple_New(extras));
8669 
8670     for ( cnt=1; *pt; ++pt ) {
8671 	if ( *pt==' ' ) {
8672 	    ++cnt;
8673 	    while ( pt[1]==' ' ) ++pt;
8674 	}
8675     }
8676     tuple = PyTuple_New(extras+cnt);
8677     for ( pt=str, cnt=0; *pt; ) {
8678 	while ( *pt==' ' ) ++pt;
8679 	if ( *pt=='\0' )
8680     break;
8681 	start = pt;
8682 	while ( *pt!=' ' && *pt!='\0' ) ++pt;
8683 	ch = *pt; *pt = '\0';
8684 	PyTuple_SetItem(tuple,extras+cnt,PyUnicode_FromString(start));
8685 	*pt = ch;
8686 	++cnt;
8687     }
8688 return( tuple );
8689 }
8690 
GlyphNamesFromTuple(PyObject * glyphs)8691 static char *GlyphNamesFromTuple(PyObject *glyphs) {
8692     int cnt, len, deltalen;
8693     const char *str;
8694     char *ret, *pt;
8695     int i;
8696 
8697     /* if called with a string, assume already in output format and return */
8698     if ( PyUnicode_Check(glyphs)) {
8699         if ((str = PyUnicode_AsUTF8(glyphs)) == NULL) {
8700             return NULL;
8701         } else if (str[0] == '\0') {
8702 	    PyErr_Format(PyExc_TypeError,"Glyph name strings may not be empty");
8703             return( NULL );
8704         }
8705         return copy(str);
8706     }
8707 
8708     if ( !PyTuple_Check(glyphs) && !PyList_Check(glyphs)) {
8709         PyErr_Format(PyExc_TypeError,"Expected tuple of glyph names");
8710         return(NULL );
8711     }
8712     cnt = PySequence_Size(glyphs);
8713     len = 0;
8714     for ( i=0; i<cnt; ++i ) {
8715 	PyObject *aglyph = PySequence_GetItem(glyphs,i);
8716 	if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(aglyph)) ) {
8717 	    SplineChar *sc = ((PyFF_Glyph *) aglyph)->sc;
8718 	    deltalen = strlen(sc->name);
8719             Py_DECREF(aglyph);
8720 	} else if ( PyUnicode_Check(aglyph)) {
8721             str = PyUnicode_AsUTF8(aglyph);
8722             if (str == NULL) {
8723                 Py_DECREF(aglyph);
8724                 return NULL;
8725             }
8726             deltalen = strlen(str);
8727             Py_DECREF(aglyph);
8728         } else {
8729             Py_XDECREF(aglyph);
8730 	    PyErr_Format(PyExc_TypeError,"Expected tuple of glyph names");
8731             return( NULL );
8732 	}
8733         if ( deltalen==0 ) {
8734 	    PyErr_Format(PyExc_TypeError,"Glyph name strings may not be empty");
8735             return( NULL );
8736         }
8737 	len += deltalen+1;
8738     }
8739 
8740     ret = pt = malloc(len+1);
8741     for ( i=0; i<cnt; ++i ) {
8742 	PyObject *aglyph = PySequence_GetItem(glyphs,i);
8743 	if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(aglyph)) ) {
8744 	    SplineChar *sc = ((PyFF_Glyph *) aglyph)->sc;
8745 	    str = sc->name;
8746 	} else {
8747         str = PyUnicode_AsUTF8(aglyph);
8748     }
8749     if (str == NULL) {
8750         Py_DECREF(aglyph);
8751         free(ret);
8752         return NULL;
8753     }
8754 	strcpy(pt,str);
8755 	Py_DECREF(aglyph);
8756 	pt += strlen(pt);
8757 	*pt++ = ' ';
8758     }
8759     if ( pt!=ret )
8760 	--pt;
8761     *pt = '\0';
8762     return( ret );
8763 }
8764 
GlyphNameArrayFromTuple(PyObject * glyphs)8765 static char **GlyphNameArrayFromTuple(PyObject *glyphs) {
8766     int cnt;
8767     char *str, **ret;
8768     int i;
8769 
8770     if ( PyUnicode_Check(glyphs) || !PySequence_Check(glyphs) ) {
8771 	PyErr_Format(PyExc_TypeError,"Expected tuple of glyph names");
8772 return(NULL );
8773     }
8774     cnt = PySequence_Size(glyphs);
8775     ret = malloc((cnt+1)*sizeof(char *));
8776     for ( i=0; i<cnt; ++i ) {
8777 	PyObject *aglyph = PySequence_GetItem(glyphs,i);
8778 	if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(aglyph)) ) {
8779 	    SplineChar *sc = ((PyFF_Glyph *) aglyph)->sc;
8780 	    str = sc->name;
8781 	} else
8782 	    str = PyBytes_AsString(aglyph);
8783 	if ( str==NULL ) {
8784 	    PyErr_Format(PyExc_TypeError,"Expected tuple of glyph names");
8785 	    free(ret);
8786 return( NULL );
8787 	}
8788 	ret[i] = copy(str);
8789     }
8790 return( ret );
8791 }
8792 
GlyphsFromTuple(SplineFont * sf,PyObject * glyphs)8793 static SplineChar **GlyphsFromTuple(SplineFont *sf, PyObject *glyphs) {
8794     int cnt;
8795     char *str, *pt, *start, ch;
8796     int i;
8797     SplineChar **ret, *sc;
8798 
8799     if ( glyphs==NULL ) {
8800 	PyErr_Format(PyExc_TypeError,"Unspecified argument." );
8801 return( NULL );
8802     }
8803     if ( PyUnicode_Check(glyphs)) {
8804 	/* A string of glyph names */
8805 	if ((str = copy(PyUnicode_AsUTF8(glyphs))) == NULL) {
8806 	    return NULL;
8807 	}
8808 	cnt = 0;
8809 	for ( pt=str; *pt==' '; ++pt );
8810 	while ( *pt!='\0' ) {
8811 	    while ( *pt!=' ' && *pt!='\0' ) ++pt;
8812 	    ++cnt;
8813 	    while ( *pt==' ' ) ++pt;
8814 	}
8815 	if ( cnt==0 ) {
8816         free(str);
8817 return( calloc(1,sizeof(SplineChar *)));
8818 	}
8819 
8820 	ret = malloc((cnt+1)*sizeof(SplineChar *));
8821 	cnt = 0;
8822 	for ( pt=str; *pt==' '; ++pt );
8823 	while ( *pt!='\0' ) {
8824 	    start = pt;
8825 	    while ( *pt!=' ' && *pt!='\0' ) ++pt;
8826 	    ch = *pt; *pt = '\0';
8827 	    sc = SFGetChar(sf,-1,start);
8828 	    if ( sc==NULL ) {
8829 		PyErr_Format(PyExc_TypeError,"String, %s, is not the name of a glyph in the expected font.", start );
8830                 free(str);
8831                 free(ret);
8832 return( NULL );
8833 	    }
8834 	    *pt = ch;
8835 	    ret[cnt++] = sc;
8836 	    while ( *pt==' ' ) ++pt;
8837 	}
8838 	ret[cnt] = NULL;
8839     free(str);
8840 return( ret );
8841     }
8842 
8843     /* A tuple or list of glyphs of glyph names */
8844     if ( !PySequence_Check(glyphs) ) {
8845 	PyErr_Format(PyExc_TypeError,"Expected tuple of glyph names");
8846 return(NULL );
8847     }
8848     cnt = PySequence_Size(glyphs);
8849     ret = malloc((cnt+1)*sizeof(SplineChar *));
8850     for ( i=0; i<cnt; ++i ) {
8851 	PyObject *aglyph = PySequence_GetItem(glyphs,i);
8852 	if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(aglyph)) ) {
8853 	    ret[i] = ((PyFF_Glyph *) aglyph)->sc;
8854 	    if ( ret[i]->parent!=sf ) {
8855 		PyErr_Format(PyExc_TypeError,"Glyph object, %s, must belong to the expected font.", ret[i]->name);
8856                 free(ret);
8857 return( NULL );
8858 	    }
8859 	} else {
8860 	    if ((str = (char*)PyUnicode_AsUTF8(aglyph)) == NULL ) { // FIXME: Free aglyph
8861 		PyErr_Format(PyExc_TypeError,"Expected a name of a glyph in the expected font." );
8862                 free(ret);
8863 return( NULL );
8864 	    }
8865 	    sc = SFGetChar(sf,-1,str);
8866 	    if ( sc==NULL ) {
8867 		PyErr_Format(PyExc_TypeError,"String, %s, is not the name of a glyph in the expected font.", str );
8868                 free(ret);
8869 return( NULL );
8870 	    }
8871 	    ret[i] = sc;
8872 	}
8873     }
8874     ret[i] = NULL;
8875 return( ret );
8876 }
8877 
PyFFGlyph_getPosSub(PyObject * self,PyObject * args)8878 static PyObject *PyFFGlyph_getPosSub(PyObject *self, PyObject *args) {
8879     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8880     SplineFont *sf = sc->parent, *sf_sl = sf;
8881     int i, j, cnt;
8882     PyObject *ret, *temp;
8883     PST *pst;
8884     KernPair *kp;
8885     struct lookup_subtable *sub;
8886     char *subname;
8887 
8888     if ( sf_sl->cidmaster!=NULL ) sf_sl = sf_sl->cidmaster;
8889     else if ( sf_sl->mm!=NULL ) sf_sl = sf_sl->mm->normal;
8890 
8891     if ( !PyArg_ParseTuple(args,"s",&subname) ) {
8892         return( NULL );
8893     }
8894     if ( *subname=='*' )
8895 	sub = NULL;
8896     else {
8897 	sub = SFFindLookupSubtable(sf,subname);
8898 	if ( sub==NULL ) {
8899 	    PyErr_Format(PyExc_KeyError, "Unknown lookup subtable: '%s'",subname);
8900 return( NULL );
8901 	}
8902     }
8903 
8904     for ( i=0; i<2; ++i ) {
8905 	cnt = 0;
8906 	for ( pst = sc->possub; pst!=NULL; pst=pst->next ) {
8907 	    if ( pst->type==pst_lcaret )
8908 	continue;
8909 	    if ( pst->subtable == sub || sub==NULL ) {
8910 		if ( i ) {
8911 		    switch ( pst->type ) {
8912 		      default:
8913 			Py_INCREF(Py_None);
8914 			PyTuple_SetItem(ret,i,Py_None);
8915 /* The important things here should not be translated. We hope the user will */
8916 /*  never see this. Let's not translate it at all */
8917 			LogError(_("Unexpected PST type in GetPosSub (%d).\n"), pst->type );
8918 		      break;
8919 		      case pst_position:
8920 			PyTuple_SetItem(ret,cnt,Py_BuildValue("(ssiiii)",
8921 				pst->subtable->subtable_name,"Position",
8922 			        pst->u.pos.xoff,pst->u.pos.yoff,
8923 			        pst->u.pos.h_adv_off, pst->u.pos.v_adv_off ));
8924 		      break;
8925 		      case pst_pair:
8926 			PyTuple_SetItem(ret,cnt,Py_BuildValue("(sssiiiiiiii)",
8927 				pst->subtable->subtable_name,"Pair",pst->u.pair.paired,
8928 			        pst->u.pair.vr[0].xoff,pst->u.pair.vr[0].yoff,
8929 			        pst->u.pair.vr[0].h_adv_off, pst->u.pair.vr[0].v_adv_off,
8930 			        pst->u.pair.vr[1].xoff,pst->u.pair.vr[1].yoff,
8931 			        pst->u.pair.vr[1].h_adv_off, pst->u.pair.vr[1].v_adv_off ));
8932 		      break;
8933 		      case pst_substitution:
8934 			PyTuple_SetItem(ret,cnt,Py_BuildValue("(sss)",
8935 				pst->subtable->subtable_name,"Substitution",pst->u.subs.variant));
8936 		      break;
8937 		      case pst_alternate:
8938 		      case pst_multiple:
8939 		      case pst_ligature:
8940 			temp = TupleOfGlyphNames(pst->u.mult.components,2);
8941 			PyTuple_SetItem(temp,0,PyUnicode_FromString(pst->subtable->subtable_name));
8942 			PyTuple_SetItem(temp,1,PyUnicode_FromString(
8943 				pst->type==pst_alternate?"AltSubs":
8944 				pst->type==pst_multiple?"MultSubs":
8945 			                    "Ligature"));
8946 			PyTuple_SetItem(ret,cnt,temp);
8947 		      break;
8948 		    }
8949 		}
8950 		++cnt;
8951 	    }
8952 	}
8953 	for ( j=0; j<2; ++j ) {
8954 	    if ( sub==NULL || sub->lookup->lookup_type==gpos_pair ) {
8955 		for ( kp= (j==0 ? sc->kerns : sc->vkerns); kp!=NULL; kp=kp->next ) {
8956 		    if ( sub==NULL || sub==kp->subtable ) {
8957 			if ( i ) {
8958 			    int xadv1, yadv1, xadv2;
8959 			    xadv1 = yadv1 = xadv2 = 0;
8960 			    if ( j )
8961 				yadv1 = kp->off;
8962 			    else
8963 				xadv1 = kp->off;
8964 			    PyTuple_SetItem(ret,cnt,Py_BuildValue("(sssiiiiiiii)",
8965 				    kp->subtable->subtable_name,"Pair",kp->sc->name,
8966 			            0,0,xadv1,yadv1,
8967 			            0,0,xadv2,0));
8968 			}
8969 			++cnt;
8970 		    }
8971 		}
8972 	    }
8973 	}
8974 	if ( i==0 )
8975 	    ret = PyTuple_New(cnt);
8976     }
8977 return( ret );
8978 }
8979 
PyFFGlyph_removePosSub(PyObject * self,PyObject * args)8980 static PyObject *PyFFGlyph_removePosSub(PyObject *self, PyObject *args) {
8981     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
8982     SplineFont *sf = sc->parent, *sf_sl = sf;
8983     int is_v;
8984     PST *pst, *next, *prev;
8985     KernPair *kp, *kpnext, *kpprev;
8986     struct lookup_subtable *sub;
8987     char *subname;
8988 
8989     if ( sf_sl->cidmaster!=NULL ) sf_sl = sf_sl->cidmaster;
8990     else if ( sf_sl->mm!=NULL ) sf_sl = sf_sl->mm->normal;
8991 
8992     if ( !PyArg_ParseTuple(args,"s",&subname) ) {
8993         return( NULL );
8994     }
8995     if ( *subname=='*' )
8996 	sub = NULL;
8997     else {
8998 	sub = SFFindLookupSubtable(sf,subname);
8999 	if ( sub==NULL ) {
9000 	    PyErr_Format(PyExc_KeyError, "Unknown lookup subtable: '%s'",subname);
9001 return( NULL );
9002 	}
9003     }
9004 
9005     for ( prev=NULL, pst = sc->possub; pst!=NULL; pst=next ) {
9006 	next = pst->next;
9007 	if ( pst->type==pst_lcaret ) {
9008 	    prev = pst;
9009     continue;
9010 	}
9011 	if ( pst->subtable == sub || sub==NULL ) {
9012 	    if ( prev==NULL )
9013 		sc->possub = next;
9014 	    else
9015 		prev->next = next;
9016 	    pst->next = NULL;
9017 	    PSTFree(pst);
9018 	} else
9019 	    prev = pst;
9020     }
9021     for ( is_v=0; is_v<2; ++is_v ) {
9022 	for ( kpprev=NULL, kp = is_v ? sc->vkerns: sc->kerns; kp!=NULL; kp=kpnext ) {
9023 	    kpnext = kp->next;
9024 	    if ( kp->subtable == sub || sub==NULL ) {
9025 		if ( kpprev!=NULL )
9026 		    kpprev->next = kpnext;
9027 		else if ( is_v )
9028 		    sc->vkerns = kpnext;
9029 		else
9030 		    sc->kerns = kpnext;
9031 		kp->next = NULL;
9032 		KernPairsFree(kp);
9033 	    } else
9034 		kpprev = kp;
9035 	}
9036     }
9037 Py_RETURN( self );
9038 }
9039 
PyFFGlyph_addPosSub(PyObject * self,PyObject * args)9040 static PyObject *PyFFGlyph_addPosSub(PyObject *self, PyObject *args) {
9041     SplineChar *sc = ((PyFF_Glyph *) self)->sc, *osc;
9042     SplineFont *sf = sc->parent, *sf_sl = sf;
9043     PST temp, *pst, *old=NULL, *prev=NULL;
9044     struct lookup_subtable *sub;
9045     const char *subname, *other;
9046     KernPair *kp, *kpold=NULL, *kpprev=NULL;
9047     PyObject *others;
9048 
9049     memset(&temp,0,sizeof(temp));
9050 
9051     if ( sf_sl->cidmaster!=NULL ) sf_sl = sf_sl->cidmaster;
9052     else if ( sf_sl->mm!=NULL ) sf_sl = sf_sl->mm->normal;
9053 
9054     if ( PySequence_Size(args)==0 ) {
9055 	PyErr_Format(PyExc_TypeError,"The first argument must be a subtable name");
9056 return( NULL );
9057     }
9058     others = PySequence_GetItem(args, 0);
9059     subname = PyUnicode_AsUTF8(others);
9060     Py_XDECREF(others);
9061     if (subname == NULL) {
9062         return NULL;
9063     }
9064     sub = SFFindLookupSubtable(sf,subname);
9065     if ( sub==NULL ) {
9066 	PyErr_Format(PyExc_KeyError, "Unknown lookup subtable: %s",subname);
9067 	return( NULL );
9068     }
9069 
9070     temp.subtable = sub;
9071 
9072     for ( old = sc->possub; old!=NULL && old->subtable!=sub; prev=old, old=old->next );
9073 
9074     if ( sub->lookup->lookup_type==gpos_single ) {
9075 	if ( !PyArg_ParseTuple(args,"shhhh", &subname,
9076 		&temp.u.pos.xoff, &temp.u.pos.yoff,
9077 		&temp.u.pos.h_adv_off, &temp.u.pos.v_adv_off))
9078 return( NULL );
9079 	temp.type = pst_position;
9080     } else if ( sub->lookup->lookup_type==gpos_pair ) {
9081 	int off =0x7fffffff;
9082 	temp.type = pst_pair;
9083 	temp.u.pair.vr = chunkalloc(sizeof(struct vr [2]));
9084 	if ( PyArg_ParseTuple(args,"ssi", &subname, &other, &off ))
9085 	    /* Good */;
9086 	else {
9087 	    off = 0x7fffffff;
9088 	    PyErr_Clear();
9089 	    if ( !PyArg_ParseTuple(args,"sshhhhhhhh", &subname, &other,
9090 		    &temp.u.pair.vr[0].xoff, &temp.u.pair.vr[0].yoff,
9091 		    &temp.u.pair.vr[0].h_adv_off, &temp.u.pair.vr[0].v_adv_off,
9092 		    &temp.u.pair.vr[1].xoff, &temp.u.pair.vr[1].yoff,
9093 		    &temp.u.pair.vr[1].h_adv_off, &temp.u.pair.vr[1].v_adv_off))
9094 return( NULL );
9095 	    if ( temp.u.pair.vr[0].xoff==0 && temp.u.pair.vr[0].yoff==0 &&
9096 		    temp.u.pair.vr[1].xoff==0 && temp.u.pair.vr[1].yoff==0 &&
9097 		    temp.u.pair.vr[1].v_adv_off==0 ) {
9098 		if ( temp.u.pair.vr[0].h_adv_off==0 && temp.u.pair.vr[1].h_adv_off==0 &&
9099 			sub->vertical_kerning )
9100 		    off = temp.u.pair.vr[0].v_adv_off;
9101 		else if ( temp.u.pair.vr[0].h_adv_off==0 && temp.u.pair.vr[0].v_adv_off==0 &&
9102 			SCRightToLeft(sc))
9103 		    off = temp.u.pair.vr[1].h_adv_off;
9104 		else if ( temp.u.pair.vr[0].v_adv_off==0 && temp.u.pair.vr[1].h_adv_off==0 )
9105 		    off = temp.u.pair.vr[0].h_adv_off;
9106 	    }
9107 	}
9108 	osc = SFGetChar(sf,-1,other);
9109 	for ( old = sc->possub; old!=NULL &&
9110 		(old->subtable!=sub || strcmp(old->u.pair.paired,other)!=0);
9111 		prev=old, old=old->next );
9112 	kpprev = NULL;
9113 	for ( kpold = sub->vertical_kerning? sc->vkerns : sc->kerns;
9114 		kpold!=NULL && (kpold->subtable!=sub || kpold->sc!=osc);
9115 		kpprev = kpold, kpold = kpold->next );
9116 	if ( off!=0x7fffffff && osc!=NULL ) {
9117 	    if ( kpold!=NULL ) {
9118 		kp = kpold;
9119 	    } else {
9120 		chunkfree(temp.u.pair.vr,sizeof(struct vr [2]));
9121 		kp = chunkalloc(sizeof(KernPair));
9122 		if ( sub->vertical_kerning ) {
9123 		    kp->next = sc->vkerns;
9124 		    sc->vkerns = kp;
9125 		} else {
9126 		    kp->next = sc->kerns;
9127 		    sc->kerns = kp;
9128 		}
9129 	    }
9130 	    kp->sc = osc;
9131 	    kp->off = off;
9132 	    kp->subtable = sub;
9133 	    if ( old!=NULL ) {
9134 		if ( prev==NULL )
9135 		    sc->possub = old->next;
9136 		else
9137 		    prev->next = old->next;
9138 		old->next = NULL;
9139 		PSTFree(old);
9140 	    }
9141 Py_RETURN( self );
9142 	}
9143 	temp.u.pair.paired = copy(other);
9144 	if ( kpold!=NULL ) {
9145 	    if ( kpprev!=NULL )
9146 		kpprev->next = kpold->next;
9147 	    else if ( sub->vertical_kerning )
9148 		sc->vkerns = kpold->next;
9149 	    else
9150 		sc->kerns = kpold->next;
9151 	    kpold->next = NULL;
9152 	    KernPairsFree(kpold);
9153 	}
9154     } else if ( sub->lookup->lookup_type==gsub_single ) {
9155 	if ( !PyArg_ParseTuple(args,"ss", &subname, &other))
9156 return( NULL );
9157 	temp.type = pst_substitution;
9158 	temp.u.subs.variant = copy(other);
9159     } else {
9160 	if ( !PyArg_ParseTuple(args,"sO", &subname, &others))
9161 return( NULL );
9162 	other = GlyphNamesFromTuple(others);
9163 	if ( other==NULL )
9164 return( NULL );
9165 	if ( sub->lookup->lookup_type==gsub_alternate )
9166 	    temp.type = pst_alternate;
9167 	else if ( sub->lookup->lookup_type==gsub_multiple )
9168 	    temp.type = pst_multiple;
9169 	else if ( sub->lookup->lookup_type==gsub_ligature ) {
9170 	    temp.type = pst_ligature;
9171 	    old = NULL;
9172 	} else {
9173 	    PyErr_Format(PyExc_KeyError, "Unexpected lookup type: %s",sub->lookup->lookup_name);
9174 return( NULL );
9175 	}
9176 	temp.u.subs.variant = (char*)other; // other holds rv from GlyphNamesFromTuple
9177     }
9178     if ( old!=NULL ) {
9179 	switch ( sub->lookup->lookup_type ) {
9180 	  case gpos_single:
9181 	    old->u.pos = temp.u.pos;
9182 	  break;
9183 	  case gpos_pair:
9184 	    chunkfree(old->u.pair.vr,sizeof(struct vr [2]));
9185 	    free(old->u.pair.paired);
9186 	    old->u.pair = temp.u.pair;
9187 	  break;
9188 	  default:
9189 	    free(old->u.subs.variant);
9190 	    old->u.subs.variant = temp.u.subs.variant;
9191 	  break;
9192 	}
9193     } else {
9194 	pst = chunkalloc(sizeof(PST));
9195 	*pst = temp;
9196 	pst->next = sc->possub;
9197 	sc->possub = pst;
9198     }
9199 Py_RETURN( self );
9200 }
9201 
PyFFGlyph_selfIntersects(PyObject * self,PyObject * UNUSED (args))9202 static PyObject *PyFFGlyph_selfIntersects(PyObject *self, PyObject *UNUSED(args)) {
9203     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
9204     int layer = ((PyFF_Glyph *) self)->layer;
9205     Spline *s, *s2;
9206     PyObject *ret;
9207     SplineSet *ss;
9208 
9209     ss = LayerAllSplines(&sc->layers[layer]);
9210     ret = SplineSetIntersect(ss,&s,&s2) ? Py_True : Py_False;
9211     LayerUnAllSplines(&sc->layers[layer]);
9212     Py_INCREF( ret );
9213 return( ret );
9214 }
9215 
PyFFGlyph_validate(PyObject * self,PyObject * args)9216 static PyObject *PyFFGlyph_validate(PyObject *self, PyObject *args) {
9217     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
9218     int force=false;
9219     int layer = ((PyFF_Glyph *) self)->layer;
9220 
9221     if ( !PyArg_ParseTuple(args,"|i",&force) )
9222 return( NULL );
9223 return( Py_BuildValue( "i", SCValidate(sc,layer,force)) );
9224 }
9225 
9226 /* Transformation flags: see 'enum fvtrans_flags' in baseviews.h */
9227 static struct flaglist trans_flags[] = {
9228     { "partialRefs", fvt_partialreftrans },
9229     { "round", fvt_round_to_int },
9230     FLAGLIST_EMPTY /* Sentinel */
9231 };
9232 
PyFFGlyph_Transform(PyObject * self,PyObject * args)9233 static PyObject *PyFFGlyph_Transform(PyObject *self, PyObject *args) {
9234     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
9235     int i;
9236     double m[6];
9237     real t[6];
9238     int flags;
9239     PyObject *flagO=NULL;
9240 
9241     if ( !PyArg_ParseTuple(args,"(dddddd)|O",&m[0], &m[1], &m[2], &m[3], &m[4], &m[5],
9242 	    &flagO) )
9243 return( NULL );
9244     flags = FlagsFromTuple(flagO,trans_flags,"transformation flag");
9245     if ( flags==FLAG_UNKNOWN )
9246 return( NULL );
9247     flags |= fvt_alllayers;
9248     for ( i=0; i<6; ++i )
9249 	t[i] = m[i];
9250     FVTrans(sc->parent->fv,sc,t,NULL,flags);
9251 Py_RETURN( self );
9252 }
9253 
PyFFGlyph_NLTransform(PyObject * self,PyObject * args)9254 static PyObject *PyFFGlyph_NLTransform(PyObject *self, PyObject *args) {
9255     SplineChar *sc = ((PyFF_Glyph *) self)->sc;
9256     char *xexpr, *yexpr;
9257 
9258     if ( !PyArg_ParseTuple(args,"ss", &xexpr, &yexpr ) )
9259 return( NULL );
9260     if ( !SCNLTrans(sc,((PyFF_Glyph *) self)->layer,xexpr,yexpr) ) {
9261 	PyErr_Format(PyExc_TypeError, "Unparseable expression.");
9262 return( NULL );
9263     }
9264 Py_RETURN( self );
9265 }
9266 
PyFFGlyph_Simplify(PyFF_Glyph * self,PyObject * args)9267 static PyObject *PyFFGlyph_Simplify(PyFF_Glyph *self, PyObject *args) {
9268     static struct simplifyinfo smpl = { sf_normal, 0.75, 0.2, 10, 0, 0, 0 };
9269     SplineChar *sc = self->sc;
9270     SplineFont *sf = sc->parent;
9271     int em = sf->ascent+sf->descent;
9272 
9273     smpl.err = em/1000.;
9274     smpl.linefixup = em/500.;
9275     smpl.linelenmax = em/100.;
9276 
9277     if ( PySequence_Size(args)>=1 )
9278 	smpl.err = PyFloat_AsDouble(PySequence_GetItem(args,0));
9279     if ( !PyErr_Occurred() && PySequence_Size(args)>=2 )
9280 	smpl.flags = FlagsFromTuple( PySequence_GetItem(args,1),simplifyflags,"simplify flag");
9281     if ( !PyErr_Occurred() && PySequence_Size(args)>=3 )
9282 	smpl.tan_bounds = PyFloat_AsDouble( PySequence_GetItem(args,2));
9283     if ( !PyErr_Occurred() && PySequence_Size(args)>=4 )
9284 	smpl.linefixup = PyFloat_AsDouble( PySequence_GetItem(args,3));
9285     if ( !PyErr_Occurred() && PySequence_Size(args)>=5 )
9286 	smpl.linelenmax = PyFloat_AsDouble( PySequence_GetItem(args,4));
9287     if ( PyErr_Occurred() )
9288 return( NULL );
9289     sc->layers[self->layer].splines = SplineCharSimplify(sc,sc->layers[self->layer].splines,&smpl);
9290     SCCharChangedUpdate(self->sc,self->layer);
9291 Py_RETURN( self );
9292 }
9293 
PyFFGlyph_Round(PyFF_Glyph * self,PyObject * args)9294 static PyObject *PyFFGlyph_Round(PyFF_Glyph *self, PyObject *args) {
9295     double factor=1;
9296 
9297     if ( !PyArg_ParseTuple(args,"|d",&factor ) )
9298 return( NULL );
9299     SCRound2Int( self->sc,self->layer,factor);
9300     SCCharChangedUpdate(self->sc,self->layer);
9301 Py_RETURN( self );
9302 }
9303 
PyFFGlyph_Cluster(PyFF_Glyph * self,PyObject * args)9304 static PyObject *PyFFGlyph_Cluster(PyFF_Glyph *self, PyObject *args) {
9305     double within = .1, max = .5;
9306 
9307     if ( !PyArg_ParseTuple(args,"|dd", &within, &max ) )
9308 return( NULL );
9309 
9310     SCRoundToCluster( self->sc,self->layer,false,within,max);
9311     SCCharChangedUpdate(self->sc,self->layer);
9312 Py_RETURN( self );
9313 }
9314 
PyFFGlyph_AddExtrema(PyFF_Glyph * self,PyObject * args)9315 static PyObject *PyFFGlyph_AddExtrema(PyFF_Glyph *self, PyObject *args) {
9316     int emsize = 1000;
9317     char *flag = NULL;
9318     int ae = ae_only_good;
9319     SplineChar *sc = self->sc;
9320     SplineFont *sf = sc->parent;
9321 
9322     if ( !PyArg_ParseTuple(args,"|si", &flag, &emsize ) )
9323 return( NULL );
9324     if ( flag!=NULL ) {
9325 	ae = FlagsFromString(flag,addextremaflags,"extrema flag");
9326 	if ( ae == FLAG_UNKNOWN )
9327 return( NULL );
9328     }
9329     SplineCharAddExtrema(sc,sc->layers[self->layer].splines,ae,sf->ascent+sf->descent);
9330     SCCharChangedUpdate(sc,self->layer);
9331 Py_RETURN( self );
9332 }
9333 
PyFFGlyph_Stroke(PyFF_Glyph * self,PyObject * args,PyObject * keywds)9334 static PyObject *PyFFGlyph_Stroke(PyFF_Glyph *self, PyObject *args, PyObject *keywds) {
9335     StrokeInfo si;
9336     SplineSet *newss;
9337 
9338     if ( Stroke_Parse(&si, args, keywds)==-1 )
9339 	return( NULL );
9340 
9341     newss = SplineSetStroke(self->sc->layers[self->layer].splines,&si,
9342 	    self->sc->layers[self->layer].order2);
9343     SplinePointListFree(self->sc->layers[self->layer].splines);
9344     self->sc->layers[self->layer].splines = newss;
9345     SCCharChangedUpdate(self->sc,self->layer);
9346     SplinePointListsFree(si.nib); si.nib = NULL;
9347 Py_RETURN( self );
9348 }
9349 
PyFFGlyph_Correct(PyFF_Glyph * self,PyObject * UNUSED (args))9350 static PyObject *PyFFGlyph_Correct(PyFF_Glyph *self, PyObject *UNUSED(args)) {
9351     int changed = false;
9352 
9353     self->sc->layers[self->layer].splines = SplineSetsCorrect(self->sc->layers[self->layer].splines,&changed);
9354     if ( changed )
9355 	SCCharChangedUpdate(self->sc,self->layer);
9356 Py_RETURN( self );
9357 }
9358 
PyFFGlyph_RemoveOverlap(PyFF_Glyph * self,PyObject * UNUSED (args))9359 static PyObject *PyFFGlyph_RemoveOverlap(PyFF_Glyph *self, PyObject *UNUSED(args)) {
9360 
9361     self->sc->layers[self->layer].splines = SplineSetRemoveOverlap(self->sc,self->sc->layers[self->layer].splines,over_remove);
9362     SCCharChangedUpdate(self->sc,self->layer);
9363 Py_RETURN( self );
9364 }
9365 
PyFFGlyph_Intersect(PyFF_Glyph * self,PyObject * UNUSED (args))9366 static PyObject *PyFFGlyph_Intersect(PyFF_Glyph *self, PyObject *UNUSED(args)) {
9367 
9368     self->sc->layers[self->layer].splines = SplineSetRemoveOverlap(self->sc,self->sc->layers[self->layer].splines,over_intersect);
9369     SCCharChangedUpdate(self->sc,self->layer);
9370 Py_RETURN( self );
9371 }
9372 
PyFFGlyph_Exclude(PyFF_Glyph * self,PyObject * args)9373 static PyObject *PyFFGlyph_Exclude(PyFF_Glyph *self, PyObject *args) {
9374     SplineSet *ss, *excludes = NULL, *tail;
9375     PyObject *obj;
9376 
9377     if ( !PyArg_ParseTuple(args,"O", &obj ) )
9378 return( NULL );
9379     if ( !PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(obj)) ) {
9380 	PyErr_Format(PyExc_TypeError, "Value must be a (FontForge) Layer");
9381 return( NULL );
9382     }
9383 
9384     excludes = SSFromLayer((PyFF_Layer *) obj);
9385     if ( PyErr_Occurred() != NULL ) {
9386 	return( NULL );
9387     }
9388     ss = self->sc->layers[self->layer].splines;
9389     for ( tail=ss; tail->next!=NULL; tail=tail->next );
9390     tail->next = excludes;
9391     while ( excludes!=NULL ) {
9392 	excludes->first->selected = true;
9393 	excludes = excludes->next;
9394     }
9395     self->sc->layers[self->layer].splines = SplineSetRemoveOverlap(NULL,ss,over_exclude);
9396     /* Frees the old splinesets */
9397     SCCharChangedUpdate(self->sc,self->layer);
9398 Py_RETURN( self );
9399 }
9400 
PyFFGlyph_preserveLayer(PyFF_Glyph * self,PyObject * args)9401 static PyObject *PyFFGlyph_preserveLayer(PyFF_Glyph *self, PyObject *args) {
9402     int layer = self->layer, dohints=false;
9403     SplineChar *sc = self->sc;
9404 
9405     if ( !PyArg_ParseTuple(args,"|ii", &layer, &dohints ) )
9406         return( NULL );
9407     if ( layer<0 || layer>=sc->layer_cnt ) {
9408         PyErr_Format(PyExc_ValueError, "Layer is out of range" );
9409         return( NULL );
9410     }
9411     _SCPreserveLayer(self->sc,layer,dohints);
9412 Py_RETURN( self );
9413 }
9414 
LayerArgToLayer(SplineFont * sf,PyObject * layerp)9415 static int LayerArgToLayer(SplineFont *sf, PyObject* layerp) {
9416     int layeri;
9417 
9418     if ( PyUnicode_Check(layerp)) {
9419         const char *name = PyUnicode_AsUTF8(layerp);
9420         if (name == NULL) {
9421             return ly_none;
9422         }
9423         layeri = SFFindLayerIndexByName(sf, name);
9424         if ( layeri<0 ) {
9425             PyErr_Format(PyExc_ValueError, "Requested layer '%s' not found", name);
9426             return ly_none;
9427         }
9428     } else if (!PyLong_Check(layerp)) {
9429         PyErr_Format(PyExc_ValueError, "First argument must be string or layer index");
9430         return ly_none;
9431     } else {
9432         layeri = PyLong_AsLong(layerp);
9433     }
9434     return layeri;
9435 }
9436 
PyFFGlyph_boundingBox(PyFF_Glyph * self,PyObject * UNUSED (args),PyObject * keywds)9437 static PyObject *PyFFGlyph_boundingBox(PyFF_Glyph *self, PyObject *UNUSED(args), PyObject *keywds) {
9438     DBounds bb;
9439     int layeri;
9440 
9441     PyObject* layerp = NULL;
9442     if (keywds != NULL) layerp = PyDict_GetItemString(keywds, "layer");
9443     int* layerp2;
9444 
9445     if (layerp != NULL) {
9446         layeri = LayerArgToLayer(self->sc->parent, layerp);
9447         if (layeri == ly_none) return NULL;
9448         SplineCharLayerFindBounds(self->sc, layeri, &bb);
9449     } else {
9450         SplineCharFindBounds(self->sc, &bb);
9451     }
9452 
9453     return( Py_BuildValue("(dddd)", bb.minx,bb.miny, bb.maxx,bb.maxy ));
9454 }
9455 
PyFF_Glyph_BoundsAt(PyCFunction bounds_func,PyFF_Glyph * self,PyObject * args,PyObject * keywds)9456 static PyObject* PyFF_Glyph_BoundsAt(PyCFunction bounds_func, PyFF_Glyph *self, PyObject *args, PyObject *keywds) {
9457     PyObject *temp = NULL;
9458     double nmin = 0, nmax = 0;
9459     double tnmin = 0, tnmax = 0;
9460     bool set = false;
9461     int layeri;
9462 
9463     PyFF_Contour* tempc = NULL;
9464 
9465     PyObject* layerp = NULL;
9466     if (keywds != NULL) layerp = PyDict_GetItemString(keywds, "layer");
9467 
9468     if (layerp != NULL) {
9469         layeri = LayerArgToLayer(self->sc->parent, layerp);
9470         if (layeri == ly_none) return NULL;
9471     }
9472 
9473     int arglen = PySequence_Size(args);
9474     int layer_cnt = self->sc->layer_cnt;
9475     for (int i = 0; i < layer_cnt; i++) {
9476         if (layerp != NULL && layeri != i) continue;
9477         SplineSet* ss = LayerAllSplines(&self->sc->layers[i]);
9478         while (ss != NULL) {
9479             tempc = ContourFromSS(ss, NULL);
9480             temp = bounds_func((PyObject*)tempc, args);
9481             ss = ss->next;
9482 
9483             Py_DECREF(tempc);
9484             if (!PyTuple_Check(temp)) {
9485                 Py_DECREF(temp);
9486                 continue;
9487             } else if (!PyArg_ParseTuple(temp, "dd", &tnmin, &tnmax)) {
9488                 IError("bounds_func returned an invalid tuple" );
9489                 Py_DECREF(temp);
9490                 return NULL;
9491             }
9492             Py_DECREF(temp);
9493 
9494             if (tnmin < nmin || !set) nmin = tnmin;
9495             if (tnmax > nmax || !set) nmax = tnmax;
9496             set = true;
9497         }
9498         LayerUnAllSplines(&self->sc->layers[i]);
9499     }
9500 
9501     if (set) {
9502         PyObject *bounds = Py_BuildValue("(dd)", nmin, nmax);
9503         return bounds;
9504     } else {
9505         Py_RETURN_NONE;
9506     }
9507 }
9508 
PyFFGlyph_xBoundsAtY(PyFF_Glyph * self,PyObject * args,PyObject * keywds)9509 static PyObject *PyFFGlyph_xBoundsAtY(PyFF_Glyph *self, PyObject *args, PyObject *keywds) {
9510     return PyFF_Glyph_BoundsAt((PyCFunction)PyFFContour_xBoundsAtY, self, args, keywds);
9511 }
9512 
PyFFGlyph_yBoundsAtX(PyFF_Glyph * self,PyObject * args,PyObject * keywds)9513 static PyObject *PyFFGlyph_yBoundsAtX(PyFF_Glyph *self, PyObject *args, PyObject *keywds) {
9514     return PyFF_Glyph_BoundsAt((PyCFunction)PyFFContour_yBoundsAtX, self, args, keywds);
9515 }
9516 
PyFFGlyph_clear(PyFF_Glyph * self,PyObject * args)9517 static PyObject *PyFFGlyph_clear(PyFF_Glyph *self, PyObject *args) {
9518 	int arglen = PySequence_Size(args);
9519     int layeri = self->layer;
9520 
9521     if (arglen <= 0) {
9522         SCClearContents(self->sc,layeri);
9523     } else if (arglen > 1) {
9524         PyErr_Format(PyExc_TypeError, "Too many arguments, only a layer index is allowed");
9525         return NULL;
9526     } else { /* arglen == 1 */
9527         PyObject *layerp = PySequence_GetItem(args,0);
9528         int layeri = LayerArgToLayer(self->sc->parent, layerp);
9529         if (layeri == ly_none) return NULL;
9530 
9531         // ly_grid not clearable with this function. In any event, it makes no
9532         // sense to put it here; clearing ly_grid should quite rightly go at
9533         // the font level since ly_grid is per-font not per-glyph so this is
9534         // not a bug
9535         if ( layeri<ly_back || layeri>=self->sc->layer_cnt ) {
9536             PyErr_Format(PyExc_ValueError, "Layer is out of range");
9537             return NULL;
9538         }
9539         SCClearLayer(self->sc, layeri);
9540     }
9541 
9542     SCCharChangedUpdate(self->sc,layeri);
9543     Py_RETURN(self);
9544 }
9545 
PyFFGlyph_isWorthOutputting(PyFF_Glyph * self,PyObject * UNUSED (args))9546 static PyObject *PyFFGlyph_isWorthOutputting(PyFF_Glyph *self, PyObject *UNUSED(args)) {
9547     PyObject *ret;
9548 
9549     ret = SCWorthOutputting(self->sc) ? Py_True : Py_False;
9550     Py_INCREF( ret );
9551 return( ret );
9552 }
9553 
PyFFGlyph_setLayer(PyFF_Glyph * self,PyObject * args)9554 static PyObject *PyFFGlyph_setLayer(PyFF_Glyph *self, PyObject *args) {
9555 	PyObject *layer, *index, *flagsobj = NULL;
9556 	int layeri, flags = 0;
9557 
9558 	if (!PyArg_ParseTuple(args, "OO|O", &layer, &index, &flagsobj)) {
9559 		PyErr_Format(PyExc_ValueError, "Must be called with a layer, a layer identifier, and an optional flags tuple" );
9560 		return NULL;
9561 	} else if ( ! (PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(layer)) || PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(layer)) ) ) {
9562 		PyErr_Format(PyExc_ValueError, "First argument must be a layer or contour" );
9563 		return NULL;
9564 	} else if ( PyUnicode_Check(index)) {
9565 		const char *name = PyUnicode_AsUTF8(index);
9566 		if (name == NULL) {
9567 			return NULL;
9568 		}
9569 		layeri = SFFindLayerIndexByName(self->sc->parent,name);
9570 		if ( layeri<0 ) {
9571 			PyErr_Format(PyExc_ValueError, "Layer '%s' not found", name);
9572 			return NULL;
9573 		}
9574 	} else if ( PyLong_Check(index)) {
9575 		layeri = PyLong_AsLong(index);
9576 	} else {
9577 		PyErr_Format(PyExc_TypeError, "Index must be a layer name or index" );
9578 		return NULL;
9579 	}
9580 	if ( flagsobj != NULL ) {
9581 		flags = FlagsFromTuple(flagsobj,pconvertflags,"Point Conversion flags");
9582 	}
9583 	flags = CheckPConvertFlags(flags, pconvert_flag_all|pconvert_flag_by_geom);
9584 	if ( flags < 0 )
9585 		return NULL;
9586 
9587 	if ( PyFF_Glyph_CSetLayer(self,layer,layeri,flags) == 0 )
9588 		Py_RETURN (self);
9589 
9590 	return NULL;
9591 }
9592 
9593 static PyMethodDef PyFF_Glyph_methods[] = {
9594     { "glyphPen", (PyCFunction) PyFFGlyph_GlyphPen, METH_VARARGS | METH_KEYWORDS, "Create a pen object which can draw into this glyph"},
9595     { "draw", (PyCFunction) PyFFGlyph_draw, METH_VARARGS , "Draw the glyph's outline to the pen argument"},
9596     { "addExtrema", (PyCFunction) PyFFGlyph_AddExtrema, METH_VARARGS, "Add extrema to the contours of the glyph"},
9597     { "addReference", PyFFGlyph_AddReference, METH_VARARGS, "Add a reference"},
9598     { "addAnchorPoint", PyFFGlyph_addAnchorPoint, METH_VARARGS, "Adds an anchor point"},
9599     { "addHint", PyFFGlyph_addHint, METH_VARARGS, "Add a postscript hint (is_vertical_hint,start_pos,width)"},
9600     { "addPosSub", PyFFGlyph_addPosSub, METH_VARARGS, "Adds position/substitution data to the glyph"},
9601     { "appendAccent", (PyCFunction) PyFFGlyph_appendAccent, METH_VARARGS | METH_KEYWORDS, "Append the named accent to the current glyph, positioning according to the rules buildAccent would use" },
9602     { "autoHint", PyFFGlyph_autoHint, METH_NOARGS, "Guess at postscript hints"},
9603     { "autoInstr", PyFFGlyph_autoInstr, METH_NOARGS, "Guess at truetype instructions"},
9604     { "autoTrace", PyFFGlyph_autoTrace, METH_NOARGS, "Autotrace any background images"},
9605     { "boundingBox", (PyCFunction) PyFFGlyph_boundingBox, METH_VARARGS | METH_KEYWORDS, "Finds the minimum bounding box for the glyph (xmin,ymin,xmax,ymax)" },
9606     { "build", PyFFGlyph_Build, METH_VARARGS, "If the current glyph is an accented character\nand all components are in the font\nthen build it out of references" },
9607     { "canonicalContours", (PyCFunction) PyFFGlyph_canonicalContours, METH_NOARGS, "Orders the contours in the current glyph by the x coordinate of their leftmost point. (This can reduce the size of the postscript charstring needed to describe the glyph(s)."},
9608     { "canonicalStart", (PyCFunction) PyFFGlyph_canonicalStart, METH_NOARGS, "Sets the start point of all the contours of the current glyph to be the leftmost point on the contour."},
9609     { "changeWeight", (PyCFunction) PyFFGlyph_changeWeight, METH_VARARGS, "Change the weight (thickness) of the stems of the glyph"},
9610     { "condenseExtend", (PyCFunction) PyFFGlyph_condenseExtend, METH_VARARGS, "Change the widths of the counters and side bearings of the glyph"},
9611     { "clear", (PyCFunction) PyFFGlyph_clear, METH_VARARGS, "Clears the contents of either a single layer of a glyph or of all the glyph's layers; makes it not worth outputting depending on its invocation" },
9612     { "cluster", (PyCFunction) PyFFGlyph_Cluster, METH_VARARGS, "Cluster the points of a glyph towards common values" },
9613     { "correctDirection", (PyCFunction) PyFFGlyph_Correct, METH_NOARGS, "Orient a layer so that external contours are clockwise and internal counter clockwise." },
9614     { "exclude", (PyCFunction) PyFFGlyph_Exclude, METH_VARARGS, "Exclude the area of the argument (a layer) from the current glyph"},
9615     { "export", (PyCFunction) PyFFGlyph_export, METH_VARARGS|METH_KEYWORDS, "Export the glyph, the format is determined by the extension. (provide the filename of the image file)" },
9616     { "genericGlyphChange", (PyCFunction) PyFFGlyph_genericGlyphChange, METH_VARARGS | METH_KEYWORDS, "Rather like changeWeight or condenseExtend, called 'Change Glyph' in UI"},
9617     { "getPosSub", PyFFGlyph_getPosSub, METH_VARARGS, "Gets position/substitution data from the glyph"},
9618     { "importOutlines", (PyCFunction) PyFFGlyph_import, METH_VARARGS|METH_KEYWORDS, "Import a background image or a foreground eps/svg/etc. (provide the filename of the image file)" },
9619     { "intersect", (PyCFunction) PyFFGlyph_Intersect, METH_NOARGS, "Leaves the areas where the contours of a glyph overlap."},
9620     { "isWorthOutputting", (PyCFunction) PyFFGlyph_isWorthOutputting, METH_NOARGS, "Returns whether the glyph is worth outputting" },
9621     { "removeOverlap", (PyCFunction) PyFFGlyph_RemoveOverlap, METH_NOARGS, "Remove overlapping areas from a glyph"},
9622     { "removePosSub", PyFFGlyph_removePosSub, METH_VARARGS, "Removes position/substitution data from the glyph"},
9623     { "round", (PyCFunction)PyFFGlyph_Round, METH_VARARGS, "Rounds point coordinates (and reference translations) to integers"},
9624     { "selfIntersects", (PyCFunction)PyFFGlyph_selfIntersects, METH_NOARGS, "Returns whether this glyph intersects itself" },
9625     { "setLayer", (PyCFunction)PyFFGlyph_setLayer, METH_VARARGS, "Replaces the content of the specified layer" },
9626     { "validate", (PyCFunction)PyFFGlyph_validate, METH_VARARGS, "Returns whether this glyph is valid for output (if not check validation_state" },
9627     { "simplify", (PyCFunction)PyFFGlyph_Simplify, METH_VARARGS, "Simplifies a glyph" },
9628     { "stroke", (PyCFunction)PyFFGlyph_Stroke, METH_VARARGS | METH_KEYWORDS, "Strokes the contours in a glyph"},
9629     { "transform", (PyCFunction)PyFFGlyph_Transform, METH_VARARGS, "Transform a glyph by a 6 element matrix." },
9630     { "nltransform", (PyCFunction)PyFFGlyph_NLTransform, METH_VARARGS, "Transform a glyph by two non-linear expressions (one for x, one for y)." },
9631     { "unlinkRef", PyFFGlyph_unlinkRef, METH_VARARGS, "Unlink a reference and turn it into outlines"},
9632     { "unlinkThisGlyph", PyFFGlyph_unlinkThisGlyph, METH_NOARGS, "Unlink all references to the current glyph in any other glyph in the font."},
9633     { "useRefsMetrics", PyFFGlyph_useRefsMetrics, METH_VARARGS, "Search the references of the current layer of the glyph and set the named reference's \"useMyMetrics\" flag."},
9634     { "xBoundsAtY", (PyCFunction)PyFFGlyph_xBoundsAtY, METH_VARARGS | METH_KEYWORDS, "The minimum and maximum values of x attained for a given y (range), or returns None"},
9635     { "yBoundsAtX", (PyCFunction)PyFFGlyph_yBoundsAtX, METH_VARARGS | METH_KEYWORDS, "The minimum and maximum values of y attained for a given x (range), or returns None"},
9636     { "preserveLayerAsUndo", (PyCFunction)PyFFGlyph_preserveLayer, METH_VARARGS, "Preserves the current layer -- as it now is -- in an undo"},
9637     PYMETHODDEF_EMPTY /* Sentinel */
9638 };
9639 /* ************************************************************************** */
9640 /*  Glyph Type  */
9641 /* ************************************************************************** */
9642 
9643 static PyTypeObject PyFF_GlyphType = {
9644     PyVarObject_HEAD_INIT(NULL, 0)
9645     "fontforge.glyph",         /* tp_name */
9646     sizeof(PyFF_Glyph),	       /* tp_basicsize */
9647     0,                         /* tp_itemsize */
9648     (destructor) PyFF_Glyph_dealloc, /* tp_dealloc */
9649     0,                         /* tp_vectorcall_offset */
9650     NULL,                      /* tp_getattr */
9651     NULL,                      /* tp_setattr */
9652     NULL,                      /* tp_reserved/tp_compare */
9653     (reprfunc) PyFFGlyph_Repr, /* tp_repr */
9654     NULL,                      /* tp_as_number */
9655     NULL,                      /* tp_as_sequence */
9656     NULL,                      /* tp_as_mapping */
9657     NULL,                      /* tp_hash */
9658     NULL,                      /* tp_call */
9659     (reprfunc) PyFFGlyph_Str,  /* tp_str */
9660     NULL,                      /* tp_getattro */
9661     NULL,                      /* tp_setattro */
9662     NULL,                      /* tp_as_buffer */
9663     Py_TPFLAGS_DEFAULT,        /* tp_flags */
9664     "FontForge Glyph object",  /* tp_doc */
9665     NULL,                      /* tp_traverse */
9666     NULL,                      /* tp_clear */
9667     (richcmpfunc)PyFFGlyph_richcompare, /* tp_richcompare */
9668     0,                         /* tp_weaklistoffset */
9669     NULL,                      /* tp_iter */
9670     NULL,                      /* tp_iternext */
9671     PyFF_Glyph_methods,        /* tp_methods */
9672     NULL,                      /* tp_members */
9673     PyFF_Glyph_getset,         /* tp_getset */
9674     NULL,                      /* tp_base */
9675     NULL,                      /* tp_dict */
9676     NULL,                      /* tp_descr_get */
9677     NULL,                      /* tp_descr_set */
9678     0,                         /* tp_dictoffset */
9679     NULL, /*(initproc)PyFF_Glyph_init*/ /* tp_init */
9680     NULL,                      /* tp_alloc */
9681     NULL, /*PyFF_Glyph_new*/   /* tp_new */
9682     NULL,                      /* tp_free */
9683     NULL,                      /* tp_is_gc */
9684     NULL,                      /* tp_bases */
9685     NULL,                      /* tp_mro */
9686     NULL,                      /* tp_cache */
9687     NULL,                      /* tp_subclasses */
9688     NULL,                      /* tp_weaklist */
9689     NULL,                      /* tp_del */
9690     0,                         /* tp_version_tag */
9691 };
9692 
9693 /* ************************************************************************** */
9694 /* Cvt iterator type */
9695 /* ************************************************************************** */
9696 
9697 typedef struct {
9698     PyObject_HEAD
9699     size_t pos;
9700     PyFF_Cvt *cvt;
9701 } cvtiterobject;
9702 static PyTypeObject PyFF_CvtIterType;
9703 
cvtiter_new(PyObject * cvt)9704 static PyObject *cvtiter_new(PyObject *cvt) {
9705     cvtiterobject *ci;
9706     ci = PyObject_New(cvtiterobject, &PyFF_CvtIterType);
9707     if (ci == NULL)
9708 return NULL;
9709     ci->cvt = ((PyFF_Cvt *) cvt);
9710     Py_INCREF(cvt);
9711     ci->pos = 0;
9712 return (PyObject *)ci;
9713 }
9714 
cvtiter_dealloc(cvtiterobject * ci)9715 static void cvtiter_dealloc(cvtiterobject *ci) {
9716     Py_XDECREF(ci->cvt);
9717     PyObject_Del(ci);
9718 }
9719 
cvtiter_iternext(cvtiterobject * ci)9720 static PyObject *cvtiter_iternext(cvtiterobject *ci) {
9721     PyFF_Cvt *cvt = ci->cvt;
9722     PyObject *entry;
9723 
9724     if ( cvt == NULL || cvt->cvt==NULL )
9725 return NULL;
9726 
9727     if ( ci->pos<cvt->cvt->len/2 ) {
9728         entry = (Py_TYPE(cvt)->tp_as_sequence->sq_item)((PyObject *) cvt, ci->pos++);
9729 return( entry );
9730     }
9731 
9732 return NULL;
9733 }
9734 
9735 static PyTypeObject PyFF_CvtIterType = {
9736     PyVarObject_HEAD_INIT(NULL, 0)
9737     "fontforge.cvt_iterator",  /* tp_name */
9738     sizeof(cvtiterobject),     /* tp_basicsize */
9739     0,                         /* tp_itemsize */
9740     /* methods */
9741     (destructor)cvtiter_dealloc, /* tp_dealloc */
9742     0,                         /* tp_vectorcall_offset */
9743     NULL,                      /* tp_getattr */
9744     NULL,                      /* tp_setattr */
9745     NULL,                      /* tp_compare */
9746     NULL,                      /* tp_repr */
9747     NULL,                      /* tp_as_number */
9748     NULL,                      /* tp_as_sequence */
9749     NULL,                      /* tp_as_mapping */
9750     NULL,                      /* tp_hash */
9751     NULL,                      /* tp_call */
9752     NULL,                      /* tp_str */
9753     NULL,                      /* tp_getattro */
9754     NULL,                      /* tp_setattro */
9755     NULL,                      /* tp_as_buffer */
9756     Py_TPFLAGS_DEFAULT,        /* tp_flags */
9757     NULL,                      /* tp_doc */
9758     NULL,                      /* tp_traverse */
9759     NULL,                      /* tp_clear */
9760     NULL,                      /* tp_richcompare */
9761     0,                         /* tp_weaklistoffset */
9762     PyObject_SelfIter,         /* tp_iter */
9763     (iternextfunc)cvtiter_iternext, /* tp_iternext */
9764     NULL,                      /* tp_methods */
9765     NULL,                      /* tp_members */
9766     PyFF_Glyph_getset,         /* tp_getset */
9767     NULL,                      /* tp_base */
9768     NULL,                      /* tp_dict */
9769     NULL,                      /* tp_descr_get */
9770     NULL,                      /* tp_descr_set */
9771     0,                         /* tp_dictoffset */
9772     NULL, /*(initproc)PyFF_Glyph_init*/ /* tp_init */
9773     NULL,                      /* tp_alloc */
9774     NULL, /*PyFF_Glyph_new*/   /* tp_new */
9775     NULL,                      /* tp_free */
9776     NULL,                      /* tp_is_gc */
9777     NULL,                      /* tp_bases */
9778     NULL,                      /* tp_mro */
9779     NULL,                      /* tp_cache */
9780     NULL,                      /* tp_subclasses */
9781     NULL,                      /* tp_weaklist */
9782     NULL,                      /* tp_del */
9783     0,                         /* tp_version_tag */
9784 };
9785 
9786 /* ************************************************************************** */
9787 /* Cvt sequence object */
9788 /* ************************************************************************** */
9789 
PyFFCvt_dealloc(PyFF_Cvt * self)9790 static void PyFFCvt_dealloc(PyFF_Cvt *self) {
9791     Py_TYPE(self)->tp_free((PyObject*)self);
9792 }
9793 
PyFFCvt_new(PyFF_Font * owner)9794 static PyObject *PyFFCvt_new(PyFF_Font *owner) {
9795     PyFF_Cvt *self;
9796 
9797     if ( CheckIfFontClosed(owner) )
9798 return( NULL );
9799 
9800     if ( owner->cvt!=NULL )
9801 Py_RETURN( owner->cvt );
9802     self = PyObject_New(PyFF_Cvt, &PyFF_CvtType);
9803     ((PyFF_Cvt *) self)->sf = owner->fv->sf;
9804     ((PyFF_Cvt *) self)->cvt = SFFindTable(self->sf,CHR('c','v','t',' '));
9805     owner->cvt = self;
9806 Py_RETURN( self );
9807 }
9808 
PyFFCvt_Str(PyFF_Cvt * self)9809 static PyObject *PyFFCvt_Str(PyFF_Cvt *self) {
9810 return( PyUnicode_FromFormat( "<cvt table for font %s>", self->sf->fontname ));
9811 }
9812 
9813 
PyFFCvt_get_font(PyFF_Cvt * self,void * UNUSED (closure))9814 static PyObject *PyFFCvt_get_font(PyFF_Cvt *self, void *UNUSED(closure)) {
9815     PyFF_Font *font = PyFF_FontForSF( self->sf );
9816     if ( font==NULL )
9817 Py_RETURN_NONE;
9818     Py_INCREF(font);
9819     return (PyObject*)font;
9820 }
9821 
9822 
9823 /* ************************************************************************** */
9824 /* Cvt sequence */
9825 /* ************************************************************************** */
9826 
PyFFCvt_Length(PyObject * self)9827 static Py_ssize_t PyFFCvt_Length( PyObject *self ) {
9828     if ( ((PyFF_Cvt *) self)->cvt==NULL )
9829 return( 0 );
9830     else
9831 return( ((PyFF_Cvt *) self)->cvt->len/2 );
9832 }
9833 
BuildCvt(SplineFont * sf,int initial_size)9834 static struct ttf_table *BuildCvt(SplineFont *sf,int initial_size) {
9835     struct ttf_table *cvt;
9836 
9837     cvt = chunkalloc(sizeof(struct ttf_table));
9838     cvt->next = sf->ttf_tables;
9839     sf->ttf_tables = cvt;
9840     cvt->tag = CHR('c','v','t',' ');
9841     cvt->data = malloc(initial_size);
9842     cvt->len = 0;
9843     cvt->maxlen = initial_size;
9844 return( cvt );
9845 }
9846 
PyFFCvt_Concat(PyObject * _c1,PyObject * _c2)9847 static PyObject *PyFFCvt_Concat( PyObject *_c1, PyObject *_c2 ) {
9848     PyFF_Cvt *c1 = (PyFF_Cvt *) _c1, *c2 = (PyFF_Cvt *) _c2;
9849     PyObject *ret;
9850     Py_ssize_t len1, len2;
9851     int i;
9852     int is_cvt2;
9853 
9854     len1 = PyFFCvt_Length(_c1);
9855     if ( PyType_IsSubtype(&PyFF_CvtType, Py_TYPE(c2)) ) {
9856 	len2 = PyFFCvt_Length(_c2);
9857 	is_cvt2 = true;
9858     } else if ( PySequence_Check(_c2)) {
9859 	is_cvt2 = false;
9860 	len2 = PySequence_Size(_c2);
9861     } else {
9862 	PyErr_Format(PyExc_TypeError, "The second argument must be either another cvt or a tuple of integers");
9863 return( NULL );
9864     }
9865     ret = PyTuple_New(len1+len2);
9866     for ( i=0; i<len1; ++i )
9867 	PyTuple_SetItem(ret,i,Py_BuildValue("i",memushort(c1->cvt->data,c1->cvt->len,i*sizeof(uint16))) );
9868     if ( is_cvt2 ) {
9869 	for ( i=0; i<len2; ++i )
9870 	    PyTuple_SetItem(ret,len1+i,Py_BuildValue("i",memushort(c2->cvt->data,c2->cvt->len,i*sizeof(uint16))) );
9871     } else {
9872 	for ( i=0; i<len2; ++i )
9873 	    PyTuple_SetItem(ret,len1+i,Py_BuildValue("i",PySequence_GetItem(_c2,i)));
9874     }
9875 Py_RETURN( (PyObject *) ret );
9876 }
9877 
PyFFCvt_InPlaceConcat(PyObject * _self,PyObject * _c2)9878 static PyObject *PyFFCvt_InPlaceConcat( PyObject *_self, PyObject *_c2 ) {
9879     PyFF_Cvt *self = (PyFF_Cvt *) _self, *c2 = (PyFF_Cvt *) _c2;
9880     int i;
9881     int is_cvt2;
9882     Py_ssize_t len1, len2;
9883     struct ttf_table *cvt;
9884 
9885     len1 = PyFFCvt_Length(_self);
9886     if ( PyType_IsSubtype(&PyFF_CvtType, Py_TYPE(c2)) ) {
9887 	len2 = PyFFCvt_Length(_c2);
9888 	is_cvt2 = true;
9889     } else if ( PySequence_Check(_c2)) {
9890 	is_cvt2 = false;
9891 	len2 = PySequence_Size(_c2);
9892     } else {
9893 	PyErr_Format(PyExc_TypeError, "The second argument must be either another cvt or a tuple of integers");
9894 return( NULL );
9895     }
9896 
9897     if ( self->cvt==NULL )
9898 	self->cvt = BuildCvt(self->sf,(len1+len2)*2);
9899     cvt = self->cvt;
9900     if ( (len1+len2)*2 >= cvt->maxlen )
9901 	cvt->data = realloc(cvt->data,cvt->maxlen = 2*(len1+len2)+10 );
9902     if ( is_cvt2 ) {
9903 	if ( len2!=0 )
9904 	    memcpy(cvt->data+len1*sizeof(uint16),c2->cvt->data, 2*len2);
9905     } else {
9906 	for ( i=0; i<len2; ++i ) {
9907 	    int val = PyLong_AsLong(PySequence_GetItem(_c2,i));
9908 	    if ( PyErr_Occurred())
9909 return( NULL );
9910 	    memputshort(cvt->data,sizeof(uint16)*(len1+i),val);
9911 	}
9912     }
9913     cvt->len += 2*len2;
9914 Py_RETURN( self );
9915 }
9916 
PyFFCvt_Index(PyObject * self,Py_ssize_t pos)9917 static PyObject *PyFFCvt_Index( PyObject *self, Py_ssize_t pos ) {
9918     PyFF_Cvt *c = (PyFF_Cvt *) self;
9919 
9920     if ( c->cvt==NULL || pos<0 || pos>=c->cvt->len/2) {
9921 	PyErr_Format(PyExc_TypeError, "Index out of bounds");
9922 return( NULL );
9923     }
9924 return( Py_BuildValue("i",(short)memushort(c->cvt->data,c->cvt->len,pos*sizeof(uint16))) );
9925 }
9926 
PyFFCvt_IndexAssign(PyObject * self,Py_ssize_t pos,PyObject * value)9927 static int PyFFCvt_IndexAssign( PyObject *self, Py_ssize_t pos, PyObject *value ) {
9928     PyFF_Cvt *c = (PyFF_Cvt *) self;
9929     struct ttf_table *cvt;
9930     int val;
9931 
9932     val = PyLong_AsLong(value);
9933     if ( PyErr_Occurred())
9934 return( -1 );
9935     if ( c->cvt==NULL )
9936 	c->cvt = BuildCvt(c->sf,2);
9937     cvt = c->cvt;
9938     if ( cvt==NULL || pos<0 || pos>cvt->len/2) {
9939 	PyErr_Format(PyExc_TypeError, "Index out of bounds");
9940 return( -1 );
9941     }
9942     if ( 2*pos>=cvt->maxlen )
9943 	cvt->data = realloc(cvt->data,cvt->maxlen = sizeof(uint16)*pos+10 );
9944     if ( 2*pos>=cvt->len )
9945 	cvt->len = sizeof(uint16)*pos;
9946     memputshort(cvt->data,sizeof(uint16)*pos,val);
9947 return( 0 );
9948 }
9949 
PyFFCvt_Slice(PyObject * self,Py_ssize_t start,Py_ssize_t end)9950 static PyObject *PyFFCvt_Slice( PyObject *self, Py_ssize_t start, Py_ssize_t end ) {
9951     PyFF_Cvt *c = (PyFF_Cvt *) self;
9952     struct ttf_table *cvt;
9953     int len, i;
9954     PyObject *ret;
9955 
9956     cvt = c->cvt;
9957     if ( cvt==NULL || end<start || end <0 || 2*start>=cvt->len ) {
9958 	PyErr_Format(PyExc_ValueError, "Slice specification out of range" );
9959 return( NULL );
9960     }
9961 
9962     len = end-start + 1;
9963 
9964     ret = PyTuple_New(len);
9965     for ( i=start; i<=end; ++i )
9966 	PyTuple_SetItem(ret,i-start,Py_BuildValue("i",memushort(cvt->data,cvt->len,2*i)));
9967 
9968 return( (PyObject *) ret );
9969 }
9970 
PyFFCvt_SliceAssign(PyObject * _self,Py_ssize_t start,Py_ssize_t end,PyObject * rpl)9971 static int PyFFCvt_SliceAssign( PyObject *_self, Py_ssize_t start, Py_ssize_t end, PyObject *rpl ) {
9972     PyFF_Cvt *c = (PyFF_Cvt *) _self;
9973     struct ttf_table *cvt;
9974     int len, i;
9975 
9976     cvt = c->cvt;
9977     if ( cvt==NULL || end<start || end <0 || 2*start>=cvt->len ) {
9978 	PyErr_Format(PyExc_ValueError, "Slice specification out of range" );
9979 return( -1 );
9980     }
9981 
9982     len = end-start + 1;
9983 
9984     if ( len!=PySequence_Size(rpl) ) {
9985 	if ( !PyErr_Occurred())
9986 	    PyErr_Format(PyExc_ValueError, "Replacement is different size than slice" );
9987 return( -1 );
9988     }
9989     for ( i=start; i<=end; ++i ) {
9990 	memputshort(cvt->data,sizeof(uint16)*i,
9991 		PyLong_AsLong(PySequence_GetItem(rpl,i-start)));
9992 	if ( PyErr_Occurred())
9993 return( -1 );
9994     }
9995 return( 0 );
9996 }
9997 
PyFFCvt_Contains(PyObject * _self,PyObject * _val)9998 static int PyFFCvt_Contains(PyObject *_self, PyObject *_val) {
9999     PyFF_Cvt *c = (PyFF_Cvt *) _self;
10000     struct ttf_table *cvt;
10001     size_t i;
10002     int val;
10003 
10004     val = PyLong_AsLong(_val);
10005     if ( PyErr_Occurred())
10006 return( -1 );
10007 
10008     cvt = c->cvt;
10009     if ( cvt==NULL )
10010 return( 0 );
10011 
10012     for ( i=0; i<cvt->len/2; ++i )
10013 	if ( memushort(cvt->data,cvt->len,2*i)==val )
10014 return( 1 );
10015 
10016 return( 0 );
10017 }
10018 
10019 static PySequenceMethods PyFFCvt_Sequence = {
10020     PyFFCvt_Length,		/* length */
10021     PyFFCvt_Concat,		/* concat */
10022     NULL,			/* repeat */
10023     PyFFCvt_Index,		/* subscript */
10024     PyFFCvt_Slice,		/* slice */
10025     PyFFCvt_IndexAssign,	/* subscript assign */
10026     PyFFCvt_SliceAssign,	/* slice assign */
10027     PyFFCvt_Contains,		/* contains */
10028     PyFFCvt_InPlaceConcat,	/* inplace_concat */
10029     NULL			/* inplace repeat */
10030 };
10031 
PyFFCvt_find(PyObject * self,PyObject * args)10032 static PyObject *PyFFCvt_find(PyObject *self, PyObject *args) {
10033     PyFF_Cvt *c = (PyFF_Cvt *) self;
10034     struct ttf_table *cvt = c->cvt;
10035     int val, low=0;
10036     size_t high, i;
10037 
10038     if ( cvt==NULL )
10039 return( Py_BuildValue("i", -1 ));
10040 
10041     high=cvt->len/2;
10042 
10043     if ( !PyArg_ParseTuple(args,"i|ii", &val, &low, &high ) )
10044 return( NULL );
10045     if ( low<0 ) low=0;
10046     if ( high>cvt->len/2 ) high = cvt->len/2;
10047     for ( i=low; i<high; ++i )
10048 	if ( (short) memushort(cvt->data,cvt->len,2*i)==val )
10049 return( Py_BuildValue("i", i ));
10050 
10051 return( Py_BuildValue("i", -1 ));
10052 }
10053 
10054 static PyGetSetDef PyFFCvt_getset[] = {
10055     {(char *)"font",
10056      (getter)PyFFCvt_get_font, NULL,
10057      (char *)"returns the font for this object", NULL},
10058     PYGETSETDEF_EMPTY /* Sentinel */
10059 };
10060 
10061 static PyMethodDef PyFFCvt_methods[] = {
10062     { (char *)"find", PyFFCvt_find, METH_VARARGS, (char *)"Finds the index in the cvt table of the specified value" },
10063     PYMETHODDEF_EMPTY /* Sentinel */
10064 };
10065 
10066 static PyTypeObject PyFF_CvtType = {
10067     PyVarObject_HEAD_INIT(NULL, 0)
10068     "fontforge.cvt",           /*tp_name*/
10069     sizeof(PyFF_Cvt),          /*tp_basicsize*/
10070     0,                         /*tp_itemsize*/
10071     (destructor)PyFFCvt_dealloc, /*tp_dealloc*/
10072     0,                         /*tp_vectorcall_offset*/
10073     NULL,                      /*tp_getattr*/
10074     NULL,                      /*tp_setattr*/
10075     NULL,                      /*tp_compare*/
10076     NULL,                      /*tp_repr*/
10077     NULL,                      /*tp_as_number*/
10078     &PyFFCvt_Sequence,         /*tp_as_sequence*/
10079     NULL,                      /*tp_as_mapping*/
10080     NULL,                      /*tp_hash */
10081     NULL,                      /*tp_call*/
10082     (reprfunc) PyFFCvt_Str,    /*tp_str*/
10083     NULL,                      /*tp_getattro*/
10084     NULL,                      /*tp_setattro*/
10085     NULL,                      /*tp_as_buffer*/
10086     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
10087     "fontforge cvt (control value table) objects", /* tp_doc */
10088     NULL,                      /* tp_traverse */
10089     NULL,                      /* tp_clear */
10090     NULL,                      /* tp_richcompare */
10091     0,                         /* tp_weaklistoffset */
10092     cvtiter_new,               /* tp_iter */
10093     NULL,                      /* tp_iternext */
10094     PyFFCvt_methods,           /* tp_methods */
10095     NULL,                      /* tp_members */
10096     PyFFCvt_getset,            /* tp_getset */
10097     NULL,                      /* tp_base */
10098     NULL,                      /* tp_dict */
10099     NULL,                      /* tp_descr_get */
10100     NULL,                      /* tp_descr_set */
10101     0,                         /* tp_dictoffset */
10102     NULL,                      /* tp_init */
10103     NULL,                      /* tp_alloc */
10104     NULL,                      /* tp_new */
10105     NULL,                      /* tp_free */
10106     NULL,                      /* tp_is_gc */
10107     NULL,                      /* tp_bases */
10108     NULL,                      /* tp_mro */
10109     NULL,                      /* tp_cache */
10110     NULL,                      /* tp_subclasses */
10111     NULL,                      /* tp_weaklist */
10112     NULL,                      /* tp_del */
10113     0,                         /* tp_version_tag */
10114 };
10115 
10116 /* ************************************************************************** */
10117 /* Selection Standard Methods */
10118 /* ************************************************************************** */
10119 
PyFFSelection_dealloc(PyFF_Selection * self)10120 static void PyFFSelection_dealloc(PyFF_Selection *self) {
10121     Py_TYPE(self)->tp_free((PyObject *) self);
10122 }
10123 
PyFFSelection_new(PyFF_Font * owner)10124 static PyObject *PyFFSelection_new(PyFF_Font *owner) {
10125     PyFF_Selection *self;
10126 
10127     if ( CheckIfFontClosed(owner) )
10128 return(NULL);
10129 
10130     if ( owner->selection != NULL )
10131 Py_RETURN( owner->selection );
10132 
10133     self = PyObject_New(PyFF_Selection, &PyFF_SelectionType);
10134     self->fv = owner->fv;
10135     owner->selection = self;
10136     self->by_glyphs = 0;
10137 Py_RETURN( self );
10138 }
10139 
PyFFSelection_Str(PyFF_Selection * self)10140 static PyObject *PyFFSelection_Str(PyFF_Selection *self) {
10141 return( PyUnicode_FromFormat( "<Selection for %s>", self->fv->sf->fontname ));
10142 }
10143 
PyFFSelection_get_font(PyFF_Selection * self,void * UNUSED (closure))10144 static PyObject *PyFFSelection_get_font(PyFF_Selection *self, void *UNUSED(closure)) {
10145     PyObject* font = PyFF_FontForFV_I( self->fv );
10146     if ( font==NULL )
10147 Py_RETURN_NONE;
10148     return font;
10149 }
10150 
PyFFSelection_ByGlyphs(PyFF_Selection * real_selection,void * UNUSED (closure))10151 static PyObject *PyFFSelection_ByGlyphs(PyFF_Selection *real_selection, void *UNUSED(closure)) {
10152     PyFF_Selection *self;
10153 
10154     self = PyObject_New(PyFF_Selection, &PyFF_SelectionType);
10155     self->fv = real_selection->fv;
10156     self->by_glyphs=1;
10157 Py_RETURN( self );
10158 }
10159 
10160 /* ************************************************************************** */
10161 /* Font Selection based methods */
10162 /* ************************************************************************** */
10163 
10164 enum { sel_more=1, sel_less=2,
10165 	sel_unicode=4, sel_encoding=8,
10166 	sel_singletons=16, sel_ranges=32 };
10167 struct flaglist select_flags[] = {
10168     { "more", sel_more },
10169     { "less", sel_less },
10170     { "unicode", sel_unicode },
10171     { "encoding", sel_encoding },
10172     { "singletons", sel_singletons },
10173     { "ranges", sel_ranges },
10174     FLAGLIST_EMPTY /* Sentinel */
10175 };
10176 
PyFFSelection_All(PyObject * self,PyObject * UNUSED (args))10177 static PyObject *PyFFSelection_All(PyObject *self, PyObject *UNUSED(args)) {
10178     FontViewBase *fv = ((PyFF_Selection *) self)->fv;
10179     int i;
10180 
10181     for ( i=0; i<fv->map->enccount; ++i )
10182 	fv->selected[i] = true;
10183 Py_RETURN(self);
10184 }
10185 
PyFFSelection_None(PyObject * self,PyObject * UNUSED (args))10186 static PyObject *PyFFSelection_None(PyObject *self, PyObject *UNUSED(args)) {
10187     FontViewBase *fv = ((PyFF_Selection *) self)->fv;
10188     int i;
10189 
10190     for ( i=0; i<fv->map->enccount; ++i )
10191 	fv->selected[i] = false;
10192 Py_RETURN(self);
10193 }
10194 
PyFFSelection_Changed(PyObject * self,PyObject * UNUSED (args))10195 static PyObject *PyFFSelection_Changed(PyObject *self, PyObject *UNUSED(args)) {
10196     FontViewBase *fv = ((PyFF_Selection *) self)->fv;
10197     int i, gid;
10198 
10199     for ( i=0; i<fv->map->enccount; ++i ) {
10200 	if ( (gid=fv->map->map[i])!=-1 && fv->sf->glyphs[gid]!=NULL )
10201 	    fv->selected[i] = fv->sf->glyphs[gid]->changed;
10202 	else
10203 	    fv->selected[i] = false;
10204     }
10205 Py_RETURN(self);
10206 }
10207 
PyFFSelection_Invert(PyObject * self,PyObject * UNUSED (args))10208 static PyObject *PyFFSelection_Invert(PyObject *self, PyObject *UNUSED(args)) {
10209     FontViewBase *fv = ((PyFF_Selection *) self)->fv;
10210     int i;
10211 
10212     for ( i=0; i<fv->map->enccount; ++i )
10213 	fv->selected[i] = !fv->selected[i];
10214 Py_RETURN(self);
10215 }
10216 
SelIndex(PyObject * arg,FontViewBase * fv,int ints_as_unicode)10217 static int SelIndex(PyObject *arg, FontViewBase *fv, int ints_as_unicode) {
10218     int enc;
10219 
10220     if ( PyUnicode_Check(arg)) {
10221 	const char *name = PyUnicode_AsUTF8(arg);
10222 	if (name == NULL) {
10223 	    return -1;
10224 	}
10225 	enc = SFFindSlot(fv->sf, fv->map, -1, name );
10226     } else if ( PyLong_Check(arg)) {
10227 	enc = PyLong_AsLong(arg);
10228 	if ( ints_as_unicode )
10229 	    enc = SFFindSlot(fv->sf, fv->map, enc, NULL );
10230     } else if ( PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(arg)) ) {
10231 	SplineChar *sc = ((PyFF_Glyph *) arg)->sc;
10232 	if ( sc->parent == fv->sf )
10233 	    enc = fv->map->backmap[sc->orig_pos];
10234 	else
10235 	    enc = SFFindSlot(fv->sf, fv->map, sc->unicodeenc, sc->name );
10236     } else {
10237 	PyErr_Format(PyExc_TypeError, "Unexpected argument type");
10238 return( -1 );
10239     }
10240     if ( enc<0 || enc>=fv->map->enccount ) {
10241 	PyErr_Format(PyExc_ValueError, "Encoding is out of range" );
10242 return( -1 );
10243     }
10244 return( enc );
10245 }
10246 
FVBDeselectAll(FontViewBase * fv)10247 static void FVBDeselectAll(FontViewBase *fv) {
10248     memset( fv->selected,0,fv->map->enccount);
10249 }
10250 
PyFFSelection_select(PyObject * self,PyObject * args)10251 static PyObject *PyFFSelection_select(PyObject *self, PyObject *args) {
10252     FontViewBase *fv = ((PyFF_Selection *) self)->fv;
10253     int flags = sel_encoding|sel_singletons;
10254     int i, j, cnt = PyTuple_Size(args);
10255     int range_started = false, range_first = -1;
10256     int enc;
10257 
10258     for ( i=0; i<cnt; ++i ) {
10259 	PyObject *arg = PyTuple_GetItem(args,i);
10260 	if ( !PyUnicode_Check(arg) && PySequence_Check(arg)) {
10261 	    int newflags = FlagsFromTuple(arg,select_flags,"select flag");
10262 	    if ( newflags==FLAG_UNKNOWN )
10263 return( NULL );
10264 	    if ( (newflags&(sel_more|sel_less)) == 0 )
10265 		newflags |= (flags&(sel_more|sel_less));
10266 	    if ( (newflags&(sel_unicode|sel_encoding)) == 0 )
10267 		newflags |= (flags&(sel_unicode|sel_encoding));
10268 	    if ( (newflags&(sel_singletons|sel_ranges)) == 0 )
10269 		newflags |= (flags&(sel_singletons|sel_ranges));
10270 	    flags = newflags;
10271 	    if ( i==0 && (flags&(sel_more|sel_less)) == 0 )
10272 		FVBDeselectAll(fv);
10273 	    range_started = false;
10274 	} else {
10275 	    if ( i==0 )
10276 		FVBDeselectAll(fv);
10277 	    enc = SelIndex(arg,fv,flags&sel_unicode);
10278 	    if ( enc==-1 )
10279 return( NULL );
10280 	    if ( flags&sel_less )
10281 		fv->selected[enc] = 0;
10282 	    else
10283 		fv->selected[enc] = 1;
10284 	    if ( flags&sel_ranges ) {
10285 		if ( !range_started ) {
10286 		    range_started = true;
10287 		    range_first = enc;
10288 		} else {
10289 		    if ( range_first>enc ) {
10290 			for ( j=enc; j<=range_first; ++j )
10291 			    fv->selected[j] = (flags&sel_less)?0:1;
10292 		    } else {
10293 			for ( j=range_first; j<=enc; ++j )
10294 			    fv->selected[j] = (flags&sel_less)?0:1;
10295 		    }
10296 		}
10297 	    }
10298 	}
10299     }
10300 
10301 Py_RETURN(self);
10302 }
10303 
10304 static PyObject *fontiter_New(PyFF_Font *font, int bysel, struct searchdata *sv);
10305 
PySelection_iter(PyObject * object)10306 static PyObject *PySelection_iter(PyObject *object) {
10307     PyFF_Selection* self = (PyFF_Selection*)object;
10308     PyFF_Font* font = (PyFF_Font*)PyFF_FontForFV(self->fv);
10309 
10310     if ( CheckIfFontClosed(font) )
10311 return (NULL);
10312 
10313     /* FontIter with either 1 => encodings of selected glyphs, or 2 => selected glyphs */
10314 return( fontiter_New(font, self->by_glyphs + 1, NULL ));
10315 }
10316 
10317 static PyMethodDef PyFFSelection_methods[] = {
10318     { "select", PyFFSelection_select, METH_VARARGS, "Selects glyphs in the font" },
10319     { "all", PyFFSelection_All, METH_NOARGS, "Selects all glyphs in the font" },
10320     { "none", PyFFSelection_None, METH_NOARGS, "Deselects everything" },
10321     { "changed", PyFFSelection_Changed, METH_NOARGS, "Selects those glyphs which have changed" },
10322     { "invert", PyFFSelection_Invert, METH_NOARGS, "Inverts the selection" },
10323     PYMETHODDEF_EMPTY /* Sentinel */
10324 };
10325 
10326 static PyGetSetDef PyFFSelection_getset[] = {
10327     {(char *)"font",
10328      (getter)PyFFSelection_get_font, NULL,
10329      (char *)"returns the font for which this is a selection", NULL},
10330     {(char *)"byGlyphs",
10331      (getter)PyFFSelection_ByGlyphs, NULL,
10332      (char *)"returns a selection object whose iterator will return glyph objects (rather than encoding indices)", NULL},
10333     PYGETSETDEF_EMPTY  /* Sentinel */
10334 };
10335 
10336 /* ************************************************************************** */
10337 /* Selection mapping */
10338 /* ************************************************************************** */
10339 
PyFFSelection_Length(PyObject * self)10340 static Py_ssize_t PyFFSelection_Length( PyObject *self ) {
10341 return( ((PyFF_Selection *) self)->fv->map->enccount );
10342 }
10343 
PyFFSelection_Index(PyObject * self,PyObject * index)10344 static PyObject *PyFFSelection_Index( PyObject *self, PyObject *index ) {
10345     PyFF_Selection *c = (PyFF_Selection *) self;
10346     PyObject *ret;
10347     int pos;
10348 
10349     pos = SelIndex(index,c->fv,false);
10350     if ( pos==-1 )
10351 return( NULL );
10352 
10353     ret = c->fv->selected[pos] ? Py_True : Py_False;
10354     Py_INCREF( ret );
10355 return( ret );
10356 }
10357 
PyFFSelection_IndexAssign(PyObject * self,PyObject * index,PyObject * value)10358 static int PyFFSelection_IndexAssign( PyObject *self, PyObject *index, PyObject *value ) {
10359     PyFF_Selection *c = (PyFF_Selection *) self;
10360     int val;
10361     int pos, cnt;
10362 
10363     if ( PySequence_Check(index)) {
10364 	cnt = PySequence_Size(index);
10365 	for ( pos=0; pos<cnt; ++pos )
10366 	    if ( PyFFSelection_IndexAssign(self,PySequence_GetItem(index,pos),value)==-1 )
10367 return( -1 );
10368 
10369 return( 0 );
10370     }
10371 
10372     pos = SelIndex(index,c->fv,false);
10373     if ( pos==-1 )
10374 return( -1 );
10375 
10376     if ( value==Py_True )
10377 	val = 1;
10378     else if ( value==Py_False )
10379 	val = 0;
10380     else {
10381 	val = PyLong_AsLong(value);
10382 	if ( PyErr_Occurred())
10383 return( -1 );
10384     }
10385     c->fv->selected[pos] = val;
10386 return( 0 );
10387 }
10388 
10389 static PyMappingMethods PyFFSelection_Mapping = {
10390     PyFFSelection_Length,		/* length */
10391     PyFFSelection_Index,		/* subscript */
10392     PyFFSelection_IndexAssign		/* subscript assign */
10393 };
10394 
10395 static PyTypeObject PyFF_SelectionType = {
10396     PyVarObject_HEAD_INIT(NULL, 0)
10397     "fontforge.selection",     /*tp_name*/
10398     sizeof(PyFF_Selection),    /*tp_basicsize*/
10399     0,                         /*tp_itemsize*/
10400     (destructor)PyFFSelection_dealloc, /*tp_dealloc*/
10401     0,                         /*tp_vectorcall_offset*/
10402     NULL,                      /*tp_getattr*/
10403     NULL,                      /*tp_setattr*/
10404     NULL,                      /*tp_compare*/
10405     NULL,                      /*tp_repr*/
10406     NULL,                      /*tp_as_number*/
10407     NULL,                      /*tp_as_sequence*/
10408     &PyFFSelection_Mapping,    /*tp_as_mapping*/
10409     NULL,                      /*tp_hash */
10410     NULL,                      /*tp_call*/
10411     (reprfunc)PyFFSelection_Str, /*tp_str*/
10412     NULL,                      /*tp_getattro*/
10413     NULL,                      /*tp_setattro*/
10414     NULL,                      /*tp_as_buffer*/
10415     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
10416     "fontforge selection objects", /* tp_doc */
10417     NULL,                      /* tp_traverse */
10418     NULL,                      /* tp_clear */
10419     NULL,                      /* tp_richcompare */
10420     0,                         /* tp_weaklistoffset */
10421     PySelection_iter,          /* tp_iter */
10422     NULL,                      /* tp_iternext */
10423     PyFFSelection_methods,     /* tp_methods */
10424     NULL,                      /* tp_members */
10425     PyFFSelection_getset,      /* tp_getset */
10426     NULL,                      /* tp_base */
10427     NULL,                      /* tp_dict */
10428     NULL,                      /* tp_descr_get */
10429     NULL,                      /* tp_descr_set */
10430     0,                         /* tp_dictoffset */
10431     NULL,                      /* tp_init */
10432     NULL,                      /* tp_alloc */
10433     NULL,                      /* tp_new */
10434     NULL,                      /* tp_free */
10435     NULL,                      /* tp_is_gc */
10436     NULL,                      /* tp_bases */
10437     NULL,                      /* tp_mro */
10438     NULL,                      /* tp_cache */
10439     NULL,                      /* tp_subclasses */
10440     NULL,                      /* tp_weaklist */
10441     NULL,                      /* tp_del */
10442     0,                         /* tp_version_tag */
10443 };
10444 
10445 
10446 /* ************************************************************************** */
10447 /* Layers info array iterator type */
10448 /* ************************************************************************** */
10449 
10450 typedef struct {
10451 	PyObject_HEAD
10452 	PyFF_LayerInfoArray *layers;
10453 	int pos;
10454 } layerinfoiterobject;
10455 static PyTypeObject PyFF_LayerInfoArrayIterType;
10456 
layerinfoiter_new(PyObject * layers)10457 static PyObject *layerinfoiter_new(PyObject *layers) {
10458     layerinfoiterobject *di;
10459     di = PyObject_New(layerinfoiterobject, &PyFF_LayerInfoArrayIterType);
10460     if (di == NULL)
10461 return NULL;
10462     Py_INCREF(layers);
10463     di->layers = (PyFF_LayerInfoArray *) layers;
10464     di->pos = 0;
10465 return (PyObject *)di;
10466 }
10467 
layerinfoiter_dealloc(layerinfoiterobject * di)10468 static void layerinfoiter_dealloc(layerinfoiterobject *di) {
10469     Py_XDECREF(di->layers);
10470     PyObject_Del(di);
10471 }
10472 
layerinfoiter_iternextkey(layerinfoiterobject * di)10473 static PyObject *layerinfoiter_iternextkey(layerinfoiterobject *di) {
10474     PyFF_LayerInfoArray *d = di->layers;
10475     SplineFont *sf;
10476 
10477     if (d == NULL )
10478 return NULL;
10479     sf = d->sf;
10480 
10481     if ( di->pos<sf->layer_cnt )
10482 return( Py_BuildValue("s",sf->layers[di->pos++].name) );
10483 
10484 return NULL;
10485 }
10486 
10487 static PyTypeObject PyFF_LayerInfoArrayIterType = {
10488     PyVarObject_HEAD_INIT(NULL, 0)
10489     "fontforge.layerinfo_array_iterator", /* tp_name */
10490     sizeof(layerinfoiterobject), /* tp_basicsize */
10491     0,                         /* tp_itemsize */
10492     /* methods */
10493     (destructor)layerinfoiter_dealloc, /* tp_dealloc */
10494     0,                         /* tp_vectorcall_offset */
10495     NULL,                      /* tp_getattr */
10496     NULL,                      /* tp_setattr */
10497     NULL,                      /* tp_compare */
10498     NULL,                      /* tp_repr */
10499     NULL,                      /* tp_as_number */
10500     NULL,                      /* tp_as_sequence */
10501     NULL,                      /* tp_as_mapping */
10502     NULL,                      /* tp_hash */
10503     NULL,                      /* tp_call */
10504     NULL,                      /* tp_str */
10505     NULL,                      /* tp_getattro */
10506     NULL,                      /* tp_setattro */
10507     NULL,                      /* tp_as_buffer */
10508     Py_TPFLAGS_DEFAULT,        /* tp_flags */
10509     NULL,                      /* tp_doc */
10510     NULL,                      /* tp_traverse */
10511     NULL,                      /* tp_clear */
10512     NULL,                      /* tp_richcompare */
10513     0,                         /* tp_weaklistoffset */
10514     PyObject_SelfIter,         /* tp_iter */
10515     (iternextfunc)layerinfoiter_iternextkey, /* tp_iternext */
10516     NULL,                      /* tp_methods */
10517     NULL,                      /* members */
10518     PyFFSelection_getset,      /* tp_getset */
10519     NULL,                      /* tp_base */
10520     NULL,                      /* tp_dict */
10521     NULL,                      /* tp_descr_get */
10522     NULL,                      /* tp_descr_set */
10523     0,                         /* tp_dictoffset */
10524     NULL,                      /* tp_init */
10525     NULL,                      /* tp_alloc */
10526     NULL,                      /* tp_new */
10527     NULL,                      /* tp_free */
10528     NULL,                      /* tp_is_gc */
10529     NULL,                      /* tp_bases */
10530     NULL,                      /* tp_mro */
10531     NULL,                      /* tp_cache */
10532     NULL,                      /* tp_subclasses */
10533     NULL,                      /* tp_weaklist */
10534     NULL,                      /* tp_del */
10535     0,                         /* tp_version_tag */
10536 };
10537 
10538 /* ************************************************************************** */
10539 /* Layer Info Standard Methods */
10540 /* ************************************************************************** */
10541 
PyFF_LayerInfo_dealloc(PyFF_LayerInfo * self)10542 static void PyFF_LayerInfo_dealloc(PyFF_LayerInfo *self) {
10543     self->sf = NULL;
10544     Py_TYPE(self)->tp_free((PyObject *) self);
10545 }
10546 
PyFFLayerInfo_Str(PyFF_LayerInfo * self)10547 static PyObject *PyFFLayerInfo_Str(PyFF_LayerInfo *self) {
10548 return( PyUnicode_FromFormat( "<LayerInfo %s,%d>",
10549 	self->sf->layers[self->layer].name,
10550 	self->sf->layers[self->layer].order2));
10551 }
10552 
PyFF_LayerInfo_get_font(PyFF_LayerInfo * self,void * UNUSED (closure))10553 static PyObject *PyFF_LayerInfo_get_font(PyFF_LayerInfo *self, void *UNUSED(closure)) {
10554     PyFF_Font *font = PyFF_FontForSF( self->sf );
10555     if ( font==NULL )
10556 Py_RETURN_NONE;
10557     Py_INCREF(font);
10558     return (PyObject*)font;
10559 }
10560 
PyFF_LayerInfo_get_name(PyFF_LayerInfo * self,void * UNUSED (closure))10561 static PyObject *PyFF_LayerInfo_get_name(PyFF_LayerInfo *self, void *UNUSED(closure)) {
10562 return( Py_BuildValue("s",self->sf->layers[self->layer].name));
10563 }
10564 
PyFF_LayerInfo_set_name(PyFF_LayerInfo * self,PyObject * value,void * UNUSED (closure))10565 static int PyFF_LayerInfo_set_name(PyFF_LayerInfo *self,PyObject *value, void *UNUSED(closure)) {
10566     char *name = copy(PyUnicode_AsUTF8(value));
10567     if (!name) {
10568         PyErr_Format(PyExc_TypeError,"Expected layer name");
10569         return -1;
10570     }
10571     free(self->sf->layers[self->layer].name);
10572     self->sf->layers[self->layer].name = name;
10573     return 0;
10574 }
10575 
PyFF_LayerInfo_get_order2(PyFF_LayerInfo * self,void * UNUSED (closure))10576 static PyObject *PyFF_LayerInfo_get_order2(PyFF_LayerInfo *self, void *UNUSED(closure)) {
10577 return( Py_BuildValue("i",self->sf->layers[self->layer].order2));
10578 }
10579 
PyFF_LayerInfo_set_order2(PyFF_LayerInfo * self,PyObject * value,void * UNUSED (closure))10580 static int PyFF_LayerInfo_set_order2(PyFF_LayerInfo *self,PyObject *value, void *UNUSED(closure)) {
10581     if ( PyLong_Check(value)) {
10582 	int val = PyLong_AsLong(value)!=0;
10583 	SplineFont *sf = self->sf;
10584 	int layer = self->layer;
10585 	if ( sf->layers[layer].order2!=val ) {
10586 	    if ( val )
10587 		SFConvertLayerToOrder2(sf,layer);
10588 	    else
10589 		SFConvertLayerToOrder3(sf,layer);
10590 	}
10591 return(0);
10592     }
10593     PyErr_Format(PyExc_TypeError,"Expected boolean value");
10594 return( -1 );
10595 }
10596 
PyFF_LayerInfo_get_background(PyFF_LayerInfo * self,void * UNUSED (closure))10597 static PyObject *PyFF_LayerInfo_get_background(PyFF_LayerInfo *self, void *UNUSED(closure)) {
10598 return( Py_BuildValue("i",self->sf->layers[self->layer].background));
10599 }
10600 
PyFF_LayerInfo_set_background(PyFF_LayerInfo * self,PyObject * value,void * UNUSED (closure))10601 static int PyFF_LayerInfo_set_background(PyFF_LayerInfo *self,PyObject *value, void *UNUSED(closure)) {
10602     if ( PyLong_Check(value)) {
10603 	int val = PyLong_AsLong(value)!=0;
10604 	SplineFont *sf = self->sf;
10605 	int layer = self->layer;
10606 	if ( val!=sf->layers[layer].background )
10607 	    SFLayerSetBackground(sf,layer,val);
10608 return(0);
10609     }
10610     PyErr_Format(PyExc_TypeError,"Expected boolean value");
10611 return( -1 );
10612 }
10613 
10614 static PyGetSetDef PyFF_LayerInfo_getset[] = {
10615     {(char *)"font",
10616      (getter)PyFF_LayerInfo_get_font, NULL,
10617      (char *)"returns the font to which this object belongs", NULL},
10618     {(char *)"name",
10619      (getter)PyFF_LayerInfo_get_name, (setter)PyFF_LayerInfo_set_name,
10620      (char *)"arbitrary (non-persistent) user data (deprecated name for temporary)", NULL},
10621     {(char *)"is_quadratic",
10622      (getter)PyFF_LayerInfo_get_order2, (setter)PyFF_LayerInfo_set_order2,
10623      (char *)"Does this layer contain quadratic or cubic splines (TrueType or PostScript)", NULL},
10624     {(char *)"is_background",
10625      (getter)PyFF_LayerInfo_get_background, (setter)PyFF_LayerInfo_set_background,
10626      (char *)"Is this a background layer or a foreground one?\nForeground layers may have fonts generated from them.\nBackground layers may contain background images", NULL},
10627     PYGETSETDEF_EMPTY /* Sentinel */
10628 };
10629 
10630 static PyTypeObject PyFF_LayerInfoType = {
10631     PyVarObject_HEAD_INIT(NULL, 0)
10632     "fontforge.layerinfo",     /* tp_name */
10633     sizeof(PyFF_LayerInfo),    /* tp_basicsize */
10634     0,                         /* tp_itemsize */
10635     (destructor) PyFF_LayerInfo_dealloc, /* tp_dealloc */
10636     0,                         /* tp_vectorcall_offset */
10637     NULL,                      /* tp_getattr */
10638     NULL,                      /* tp_setattr */
10639     NULL,                      /* tp_compare */
10640     NULL,                      /* tp_repr */
10641     NULL,                      /* tp_as_number */
10642     NULL,                      /* tp_as_sequence */
10643     NULL,                      /* tp_as_mapping */
10644     NULL,                      /* tp_hash */
10645     NULL,                      /* tp_call */
10646     (reprfunc) PyFFLayerInfo_Str, /* tp_str */
10647     NULL,                      /* tp_getattro */
10648     NULL,                      /* tp_setattro */
10649     NULL,                      /* tp_as_buffer */
10650     Py_TPFLAGS_DEFAULT,        /* tp_flags */
10651     "FontForge layer info",    /* tp_doc */
10652     NULL,                      /* tp_traverse */
10653     NULL,                      /* tp_clear */
10654     NULL,                      /* tp_richcompare */
10655     0,                         /* tp_weaklistoffset */
10656     NULL,                      /* tp_iter */
10657     NULL,                      /* tp_iternext */
10658     NULL,                      /* tp_methods */
10659     NULL,                      /* tp_members */
10660     PyFF_LayerInfo_getset,     /* tp_getset */
10661     NULL,                      /* tp_base */
10662     NULL,                      /* tp_dict */
10663     NULL,                      /* tp_descr_get */
10664     NULL,                      /* tp_descr_set */
10665     0,                         /* tp_dictoffset */
10666     NULL,                      /* tp_init */
10667     NULL,                      /* tp_alloc */
10668     NULL,                      /* tp_new */
10669     NULL,                      /* tp_free */
10670     NULL,                      /* tp_is_gc */
10671     NULL,                      /* tp_bases */
10672     NULL,                      /* tp_mro */
10673     NULL,                      /* tp_cache */
10674     NULL,                      /* tp_subclasses */
10675     NULL,                      /* tp_weaklist */
10676     NULL,                      /* tp_del */
10677     0,                         /* tp_version_tag */
10678 };
10679 
10680 /* ************************************************************************** */
10681 /* Layers Info Array Standard Methods */
10682 /* ************************************************************************** */
10683 
PyFF_LayerInfoArray_dealloc(PyFF_LayerInfoArray * self)10684 static void PyFF_LayerInfoArray_dealloc(PyFF_LayerInfoArray *self) {
10685     self->sf = NULL;
10686     Py_TYPE(self)->tp_free((PyObject *) self);
10687 }
10688 
PyFFLayerInfoArray_Str(PyFF_LayerInfoArray * self)10689 static PyObject *PyFFLayerInfoArray_Str(PyFF_LayerInfoArray *self) {
10690 return( PyUnicode_FromFormat( "<Layer Info Array for %s>", self->sf->fontname ));
10691 }
10692 
PyFFLayerInfoArray_get_font(PyFF_LayerInfoArray * self,void * UNUSED (closure))10693 static PyObject *PyFFLayerInfoArray_get_font(PyFF_LayerInfoArray *self, void *UNUSED(closure)) {
10694     PyFF_Font *font = PyFF_FontForSF( self->sf );
10695     if ( font==NULL )
10696 Py_RETURN_NONE;
10697     Py_INCREF(font);
10698     return (PyObject*)font;
10699 }
10700 
10701 /* ************************************************************************** */
10702 /* ****************************** Layers Array ****************************** */
10703 /* ************************************************************************** */
10704 
PyFF_LayerInfoArrayLength(PyObject * self)10705 static Py_ssize_t PyFF_LayerInfoArrayLength( PyObject *self ) {
10706     SplineFont *sf = ((PyFF_LayerInfoArray *) self)->sf;
10707     if ( sf==NULL )
10708 return( 0 );
10709     else
10710 return( sf->layer_cnt );
10711 }
10712 
PyFF_LayerInfoArrayIndex(PyObject * self,PyObject * index)10713 static PyObject *PyFF_LayerInfoArrayIndex( PyObject *self, PyObject *index ) {
10714     SplineFont *sf = ((PyFF_LayerInfoArray *) self)->sf;
10715     int layer;
10716     PyFF_LayerInfo *li;
10717 
10718     if ( PyUnicode_Check(index)) {
10719 	const char *name = PyUnicode_AsUTF8(index);
10720 	if (name == NULL) {
10721 	    return NULL;
10722 	}
10723 	layer = SFFindLayerIndexByName(sf,name);
10724 	if ( layer<0 )
10725 return( NULL );
10726     } else if ( PyLong_Check(index)) {
10727 	layer = PyLong_AsLong(index);
10728     } else {
10729 	PyErr_Format(PyExc_TypeError, "Index must be a layer name or index" );
10730 return( NULL );
10731     }
10732     if ( layer<0 || layer>=sf->layer_cnt ) {
10733 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
10734 return( NULL );
10735     }
10736     li = PyObject_New(PyFF_LayerInfo, &PyFF_LayerInfoType);
10737     li->sf = sf;
10738     li->layer = layer;
10739 return( (PyObject *) li );
10740 }
10741 
PyFF_LayerInfoArrayIndexAssign(PyObject * self,PyObject * index,PyObject * value)10742 static int PyFF_LayerInfoArrayIndexAssign( PyObject *self, PyObject *index, PyObject *value ) {
10743     SplineFont *sf = ((PyFF_LayerInfoArray *) self)->sf;
10744     int layer, order2;
10745     char *name;
10746 
10747     if ( PyUnicode_Check(index)) {
10748 	const char *name = PyUnicode_AsUTF8(index);
10749 	if (name == NULL) {
10750 	    return -1;
10751 	}
10752 	layer = SFFindLayerIndexByName(sf,name);
10753 	if ( layer<0 )
10754 return( -1 );
10755     } else if ( PyLong_Check(index)) {
10756 	layer = PyLong_AsLong(index);
10757     } else {
10758 	PyErr_Format(PyExc_TypeError, "Index must be a layer name or index" );
10759 return( -1 );
10760     }
10761     if ( layer<0 || layer>=sf->layer_cnt ) {
10762 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
10763 return( -1 );
10764     }
10765     if ( value==NULL ) {
10766 	if ( layer>ly_fore )
10767 	    SFRemoveLayer(sf,layer);
10768 	else {
10769 	    PyErr_Format(PyExc_ValueError, "You may not delete the background or foreground layers" );
10770 return( -1 );
10771 	}
10772 return( 0 );
10773     } else if ( !PyArg_ParseTuple(value,"si", &name, &order2 ) )
10774 return( -1 );
10775     free(sf->layers[layer].name);
10776     sf->layers[layer].name = copy(name);
10777     order2 = order2!=0;
10778     if ( sf->layers[layer].order2!=order2 ) {
10779 	if ( sf->layers[layer].order2!=order2 ) {
10780 	    if ( order2 )
10781 		SFConvertLayerToOrder2(sf,layer);
10782 	    else
10783 		SFConvertLayerToOrder3(sf,layer);
10784 	}
10785     }
10786 return( 0 );
10787 }
10788 
10789 static PyMappingMethods PyFF_LayerInfoArrayMapping = {
10790     PyFF_LayerInfoArrayLength,		/* length */
10791     PyFF_LayerInfoArrayIndex,		/* subscript */
10792     PyFF_LayerInfoArrayIndexAssign	/* subscript assign */
10793 };
10794 
PyFF_LayerInfoArray_add(PyObject * self,PyObject * args)10795 static PyObject *PyFF_LayerInfoArray_add(PyObject *self, PyObject *args) {
10796     SplineFont *sf = ((PyFF_LayerInfoArray *) self)->sf;
10797     int order2, background=0;
10798     char *name;
10799 
10800     if ( !PyArg_ParseTuple(args,"si|i", &name, &order2, &background ) )
10801 return( NULL );
10802     SFAddLayer(sf,name,order2,background);
10803 	CVLayerPaletteCheck(sf);
10804 Py_RETURN(self);
10805 }
10806 
10807 static PyGetSetDef PyFF_LayerInfoArray_getset[] = {
10808     {(char *)"font",
10809      (getter)PyFFLayerInfoArray_get_font, NULL,
10810      (char *)"returns the font for this object", NULL},
10811     PYGETSETDEF_EMPTY  /* Sentinel */
10812 };
10813 
10814 static PyMethodDef PyFF_LayerInfoArray_methods[] = {
10815     { "add", PyFF_LayerInfoArray_add, METH_VARARGS, "Adds a new layer to the font" },
10816     PYMETHODDEF_EMPTY /* Sentinel */
10817 };
10818 
10819 static PyTypeObject PyFF_LayerInfoArrayType = {
10820     PyVarObject_HEAD_INIT(NULL, 0)
10821     "fontforge.layerinfo_array",/*tp_name*/
10822     sizeof(PyFF_LayerInfoArray), /*tp_basicsize*/
10823     0,                         /*tp_itemsize*/
10824     (destructor) PyFF_LayerInfoArray_dealloc, /*tp_dealloc*/
10825     0,                         /*tp_vectorcall_offset*/
10826     NULL,                      /*tp_getattr*/
10827     NULL,                      /*tp_setattr*/
10828     NULL,                      /*tp_compare*/
10829     NULL,                      /*tp_repr*/
10830     NULL,                      /*tp_as_number*/
10831     NULL,                      /*tp_as_sequence*/
10832     &PyFF_LayerInfoArrayMapping, /*tp_as_mapping*/
10833     NULL,                      /*tp_hash */
10834     NULL,                      /*tp_call*/
10835     (reprfunc) PyFFLayerInfoArray_Str, /*tp_str*/
10836     NULL,                      /*tp_getattro*/
10837     NULL,                      /*tp_setattro*/
10838     NULL,                      /*tp_as_buffer*/
10839     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
10840     "FontForge layers array",  /* tp_doc */
10841     NULL,                      /* tp_traverse */
10842     NULL,                      /* tp_clear */
10843     NULL,                      /* tp_richcompare */
10844     0,                         /* tp_weaklistoffset */
10845     layerinfoiter_new,         /* tp_iter */
10846     NULL,                      /* tp_iternext */
10847     PyFF_LayerInfoArray_methods, /* tp_methods */
10848     NULL,                      /* tp_members */
10849     PyFF_LayerInfoArray_getset, /* tp_getset */
10850     NULL,                      /* tp_base */
10851     NULL,                      /* tp_dict */
10852     NULL,                      /* tp_descr_get */
10853     NULL,                      /* tp_descr_set */
10854     0,                         /* tp_dictoffset */
10855     NULL,                      /* tp_init */
10856     NULL,                      /* tp_alloc */
10857     NULL,                      /* tp_new */
10858     NULL,                      /* tp_free */
10859     NULL,                      /* tp_is_gc */
10860     NULL,                      /* tp_bases */
10861     NULL,                      /* tp_mro */
10862     NULL,                      /* tp_cache */
10863     NULL,                      /* tp_subclasses */
10864     NULL,                      /* tp_weaklist */
10865     NULL,                      /* tp_del */
10866     0,                         /* tp_version_tag */
10867 };
10868 
10869 /* ************************************************************************** */
10870 /* Font math constants type */
10871 /* ************************************************************************** */
10872 
PyFFMath_dealloc(PyFF_Math * self)10873 static void PyFFMath_dealloc(PyFF_Math *self) {
10874     Py_TYPE(self)->tp_free((PyObject*)self);
10875 }
10876 
PyFFMath_Str(PyFF_Math * self)10877 static PyObject *PyFFMath_Str(PyFF_Math *self) {
10878 return( PyUnicode_FromFormat( "<math table for font %s>", self->sf->fontname ));
10879 }
10880 
SFGetMathTable(SplineFont * sf)10881 static struct MATH *SFGetMathTable(SplineFont *sf) {
10882     if ( sf->cidmaster!=NULL )
10883 	sf = sf->cidmaster;
10884     if ( sf->MATH==NULL )
10885 	sf->MATH = MathTableNew(sf);
10886 return(sf->MATH);
10887 }
10888 
PyFFMath_get(PyFF_Math * self,void * closure)10889 static PyObject *PyFFMath_get(PyFF_Math *self, void *closure) {
10890     int offset = (int) (intpt) closure;
10891     struct MATH *math;
10892 
10893     math = SFGetMathTable(self->sf);
10894 	/* some entries are unsigned, but I don't know which here */
10895 return( Py_BuildValue("i", *(int16 *) (((char *) math) + offset) ));
10896 }
10897 
PyFFMath_set(PyFF_Math * self,PyObject * value,void * closure)10898 static int PyFFMath_set(PyFF_Math *self, PyObject *value, void *closure) {
10899     int offset = (int) (intpt) closure;
10900     struct MATH *math;
10901     long val;
10902 
10903     math = SFGetMathTable(self->sf);
10904     val = PyLong_AsLong(value);
10905     if ( val==-1 && PyErr_Occurred())
10906 return( -1 );
10907     if ( val<-32768 || val>65535 ) {
10908 	PyErr_Format(PyExc_ValueError, "The math table constants must have 16 bit values, but this (%ld) is out of range", val);
10909 return( -1 );
10910     }
10911     *(int16 *) (((char *) math) + offset) = val;
10912 return( 0 );
10913 }
10914 
PyFFMath_clear(PyFF_Math * self,PyObject * UNUSED (args))10915 static PyObject *PyFFMath_clear(PyFF_Math *self, PyObject *UNUSED(args)) {
10916     SplineFont *sf = self->sf;
10917     if ( sf->cidmaster!=NULL )
10918 	sf = sf->cidmaster;
10919     if ( sf->MATH!=NULL ) {
10920 	MATHFree(sf->MATH);
10921 	sf->MATH = NULL;
10922     }
10923 Py_RETURN( self );
10924 }
10925 
PyFFMath_exists(PyFF_Math * self,PyObject * UNUSED (args))10926 static PyObject *PyFFMath_exists(PyFF_Math *self, PyObject *UNUSED(args)) {
10927     PyObject *ret;
10928     SplineFont *sf = self->sf;
10929     if ( sf->cidmaster!=NULL )
10930 	sf = sf->cidmaster;
10931     ret = self->sf->MATH!=NULL ? Py_True : Py_False;
10932     Py_INCREF( ret );
10933 return( ret );
10934 }
10935 
10936 static PyMethodDef FFMath_methods[] = {
10937     {"clear", (PyCFunction)PyFFMath_clear, METH_NOARGS,
10938 	     "Removes any (underlying) math table from this font. Referencing a member will create the table again." },
10939     {"exists", (PyCFunction)PyFFMath_exists, METH_NOARGS,
10940 	     "Returns whether there is an underlying table associated with this font. Referencing a member will create the table if it does not exist." },
10941     PYMETHODDEF_EMPTY /* Sentinel */
10942 };
10943 
10944 static PyTypeObject PyFF_MathType = {
10945     PyVarObject_HEAD_INIT(NULL, 0)
10946     "fontforge.math",	       /* tp_name */
10947     sizeof(PyFF_Math),         /* tp_basicsize */
10948     0,                         /* tp_itemsize */
10949     (destructor)PyFFMath_dealloc, /*tp_dealloc */
10950     0,                         /* tp_vectorcall_offset */
10951     NULL,                      /* tp_getattr */
10952     NULL,                      /* tp_setattr */
10953     NULL,                      /* tp_compare */
10954     NULL,                      /* tp_repr */
10955     NULL,                      /* tp_as_number */
10956     NULL,                      /* tp_as_sequence */
10957     NULL,                      /* tp_as_mapping */
10958     NULL,                      /* tp_hash */
10959     NULL,                      /* tp_call */
10960     (reprfunc) PyFFMath_Str,   /* tp_str */
10961     NULL,                      /* tp_getattro */
10962     NULL,                      /* tp_setattro */
10963     NULL,                      /* tp_as_buffer */
10964     Py_TPFLAGS_DEFAULT,        /* tp_flags */
10965     "fontforge math objects",  /* tp_doc */
10966     NULL,                      /* tp_traverse */
10967     NULL,                      /* tp_clear */
10968     NULL,                      /* tp_richcompare */
10969     0,                         /* tp_weaklistoffset */
10970     NULL,                      /* tp_iter */
10971     NULL,                      /* tp_iternext */
10972     FFMath_methods,            /* tp_methods */
10973     NULL,                      /* tp_members */
10974     NULL, /* Will build this when readying the type*/ /* tp_getset */
10975     NULL,                      /* tp_base */
10976     NULL,                      /* tp_dict */
10977     NULL,                      /* tp_descr_get */
10978     NULL,                      /* tp_descr_set */
10979     0,                         /* tp_dictoffset */
10980     NULL,                      /* tp_init */
10981     NULL,                      /* tp_alloc */
10982     NULL,                      /* tp_new */
10983     NULL,                      /* tp_free */
10984     NULL,                      /* tp_is_gc */
10985     NULL,                      /* tp_bases */
10986     NULL,                      /* tp_mro */
10987     NULL,                      /* tp_cache */
10988     NULL,                      /* tp_subclasses */
10989     NULL,                      /* tp_weaklist */
10990     NULL,                      /* tp_del */
10991     0,                         /* tp_version_tag */
10992 };
10993 
10994 /* Build a get/set table for the math type based on the math_constants_descriptor */
setup_math_type(PyTypeObject * mathtype)10995 static int setup_math_type(PyTypeObject* mathtype) {
10996     int cnt;
10997     PyGetSetDef *getset;
10998 
10999     for ( cnt=0; math_constants_descriptor[cnt].script_name!=NULL; ++cnt );
11000 
11001     getset = calloc(cnt+1,sizeof(PyGetSetDef));
11002     for ( cnt=0; math_constants_descriptor[cnt].script_name!=NULL; ++cnt ) {
11003 	getset[cnt].name = math_constants_descriptor[cnt].script_name;
11004 	getset[cnt].get = (getter) PyFFMath_get;
11005 	getset[cnt].set = (setter) PyFFMath_set;
11006 	getset[cnt].doc = math_constants_descriptor[cnt].message;
11007 	getset[cnt].closure = (void *) (intpt) math_constants_descriptor[cnt].offset;
11008     }
11009     mathtype->tp_getset = getset;
11010     return 0;
11011 }
11012 
11013 /* ************************************************************************** */
11014 /* Private dictionary iterator type */
11015 /* ************************************************************************** */
11016 
11017 typedef struct {
11018 	PyObject_HEAD
11019 	PyFF_Private *private;
11020 	int pos;
11021 } privateiterobject;
11022 static PyTypeObject PyFF_PrivateIterType;
11023 
privateiter_new(PyObject * private)11024 static PyObject *privateiter_new(PyObject *private) {
11025     privateiterobject *di;
11026     di = PyObject_New(privateiterobject, &PyFF_PrivateIterType);
11027     if (di == NULL)
11028 return NULL;
11029     Py_INCREF(private);
11030     di->private = (PyFF_Private *) private;
11031     di->pos = 0;
11032 return (PyObject *)di;
11033 }
11034 
privateiter_dealloc(privateiterobject * di)11035 static void privateiter_dealloc(privateiterobject *di) {
11036     Py_XDECREF(di->private);
11037     PyObject_Del(di);
11038 }
11039 
privateiter_iternextkey(privateiterobject * di)11040 static PyObject *privateiter_iternextkey(privateiterobject *di) {
11041     PyFF_Private *d = di->private;
11042 
11043     if (d == NULL || d->sf->private==NULL )
11044 return NULL;
11045 
11046     if ( di->pos<d->sf->private->next )
11047 return( Py_BuildValue("s",d->sf->private->keys[di->pos++]) );
11048 
11049 return NULL;
11050 }
11051 
11052 static PyTypeObject PyFF_PrivateIterType = {
11053     PyVarObject_HEAD_INIT(NULL, 0)
11054     "fontforge.private_iterator",  /* tp_name */
11055     sizeof(privateiterobject), /* tp_basicsize */
11056     0,                         /* tp_itemsize */
11057     /* methods */
11058     (destructor)privateiter_dealloc, /* tp_dealloc */
11059     0,                         /* tp_vectorcall_offset */
11060     NULL,                      /* tp_getattr */
11061     NULL,                      /* tp_setattr */
11062     NULL,                      /* tp_compare */
11063     NULL,                      /* tp_repr */
11064     NULL,                      /* tp_as_number */
11065     NULL,                      /* tp_as_sequence */
11066     NULL,                      /* tp_as_mapping */
11067     NULL,                      /* tp_hash */
11068     NULL,                      /* tp_call */
11069     NULL,                      /* tp_str */
11070     NULL,                      /* tp_getattro */
11071     NULL,                      /* tp_setattro */
11072     NULL,                      /* tp_as_buffer */
11073     Py_TPFLAGS_DEFAULT,        /* tp_flags */
11074     NULL,                      /* tp_doc */
11075     NULL,                      /* tp_traverse */
11076     NULL,                      /* tp_clear */
11077     NULL,                      /* tp_richcompare */
11078     0,                         /* tp_weaklistoffset */
11079     PyObject_SelfIter,         /* tp_iter */
11080     (iternextfunc)privateiter_iternextkey, /* tp_iternext */
11081     NULL,                      /* tp_methods */
11082     NULL,                      /* tp_members */
11083     NULL,                      /* tp_getset */
11084     NULL,                      /* tp_base */
11085     NULL,                      /* tp_dict */
11086     NULL,                      /* tp_descr_get */
11087     NULL,                      /* tp_descr_set */
11088     0,                         /* tp_dictoffset */
11089     NULL,                      /* tp_init */
11090     NULL,                      /* tp_alloc */
11091     NULL,                      /* tp_new */
11092     NULL,                      /* tp_free */
11093     NULL,                      /* tp_is_gc */
11094     NULL,                      /* tp_bases */
11095     NULL,                      /* tp_mro */
11096     NULL,                      /* tp_cache */
11097     NULL,                      /* tp_subclasses */
11098     NULL,                      /* tp_weaklist */
11099     NULL,                      /* tp_del */
11100     0,                         /* tp_version_tag */
11101 };
11102 
11103 /* ************************************************************************** */
11104 /* Private Dict Standard Methods */
11105 /* ************************************************************************** */
11106 
PyFF_Private_dealloc(PyFF_Private * self)11107 static void PyFF_Private_dealloc(PyFF_Private *self) {
11108     self->sf = NULL;
11109     Py_TYPE(self)->tp_free((PyObject *) self);
11110 }
11111 
PyFFPrivate_Str(PyFF_Private * self)11112 static PyObject *PyFFPrivate_Str(PyFF_Private *self) {
11113 return( PyUnicode_FromFormat( "<Private Dictionary for %s>", self->sf->fontname ));
11114 }
11115 
11116 /* ************************************************************************** */
11117 /* ************************** Private Dictionary **************************** */
11118 /* ************************************************************************** */
11119 
PyFF_PrivateLength(PyObject * self)11120 static Py_ssize_t PyFF_PrivateLength( PyObject *self ) {
11121     struct psdict *private = ((PyFF_Private *) self)->sf->private;
11122     if ( private==NULL )
11123 return( 0 );
11124     else
11125 return( private->next );
11126 }
11127 
PyFF_PrivateIndex(PyObject * self,PyObject * index)11128 static PyObject *PyFF_PrivateIndex( PyObject *self, PyObject *index ) {
11129     SplineFont *sf = ((PyFF_Private *) self)->sf;
11130     struct psdict *private = sf->private;
11131     char *value=NULL;
11132     char *pt, *end;
11133     double temp;
11134     PyObject *tuple;
11135 
11136     if ( PyUnicode_Check(index)) {
11137 	const char *name = PyUnicode_AsUTF8(index);
11138 	if (name == NULL) {
11139 	    return NULL;
11140 	}
11141 	if ( private!=NULL )
11142 	    value = PSDictHasEntry(private,name);
11143     } else {
11144 	PyErr_Format(PyExc_TypeError, "Private dictionary index must be a string" );
11145 return( NULL );
11146     }
11147     if ( value==NULL ) {
11148 	PyErr_Format(PyExc_TypeError, "No such dictionary entry for specified index" );
11149 return( NULL );
11150     }
11151     strtod(value,&end); while ( *end==' ' ) ++end;
11152     if ( *end=='\0' )
11153 return( Py_BuildValue("d",strtod(value,NULL)) );
11154 
11155     if ( *value=='[' ) {
11156 	int cnt = 0;
11157 	pt = value+1;
11158 	for (;;) {
11159 	    strtod(pt,&end);
11160 	    if ( pt==end )
11161 	break;
11162 	    ++cnt;
11163 	    pt = end;
11164 	}
11165 	while ( *pt==' ' ) ++pt;
11166 	if ( *pt==']' ) {
11167 	    tuple = PyTuple_New(cnt);
11168 	    cnt = 0;
11169 	    pt = value+1;
11170 	    for (;;) {
11171 		temp = strtod(pt,&end);
11172 		if ( pt==end )
11173 	    break;
11174 		PyTuple_SetItem(tuple,cnt++,Py_BuildValue("d",temp));
11175 		pt = end;
11176 	    }
11177 return( tuple );
11178 	}
11179     }
11180 return( Py_BuildValue("s",value));
11181 }
11182 
PyFF_PrivateIndexAssign(PyObject * self,PyObject * index,PyObject * value)11183 static int PyFF_PrivateIndexAssign( PyObject *self, PyObject *index, PyObject *value ) {
11184     SplineFont *sf = ((PyFF_Private *) self)->sf;
11185     struct psdict *private = sf->private;
11186     const char *string, *name;
11187     char *freeme = NULL;
11188     char buffer[40];
11189 
11190     if ( PyUnicode_Check(value)) {
11191         string = PyUnicode_AsUTF8(value);
11192         if (string == NULL) {
11193             return -1;
11194         }
11195     } else if ( PyFloat_Check(value)) {
11196         string = buffer;
11197         g_ascii_formatd(buffer, sizeof(buffer), "%g", PyFloat_AsDouble(value));
11198     } else if ( PyLong_Check(value)) {
11199         string = buffer;
11200         snprintf(buffer, sizeof(buffer), "%ld", PyLong_AsLong(value));
11201     } else if ( PySequence_Check(value)) {
11202 	char *pt;
11203 	int i, size = PySequence_Size(value);
11204 	string = pt = freeme = malloc(size*21+4);
11205 	*pt++ = '[';
11206 	for ( i=0; i<size; ++i ) {
11207 	    PyObject *obj = PySequence_GetItem(value, i);
11208 	    g_ascii_formatd(pt, 21, "%g", PyFloat_AsDouble(obj));
11209 	    Py_DECREF(obj);
11210 	    pt += strlen(pt);
11211 	    *pt++ = ' ';
11212 	}
11213 	if ( pt[-1]==' ' ) --pt;
11214 	*pt++ = ']'; *pt = '\0';
11215     } else {
11216 	PyErr_Format(PyExc_TypeError, "Assignment value must be string, float, integer or tuple" );
11217         return( -1 );
11218     }
11219 
11220     name = PyUnicode_AsUTF8(index);
11221     if (name == NULL) {
11222         PyErr_Format(PyExc_TypeError, "Private dictionary index must be a string" );
11223         free(freeme);
11224         return -1;
11225     } else if (private == NULL) {
11226         sf->private = private = calloc(1,sizeof(struct psdict));
11227     }
11228 
11229     PSDictChangeEntry(private,name,string);
11230     free(freeme);
11231     return( 0 );
11232 }
11233 
11234 static PyMappingMethods PyFF_PrivateMapping = {
11235     PyFF_PrivateLength,		/* length */
11236     PyFF_PrivateIndex,		/* subscript */
11237     PyFF_PrivateIndexAssign	/* subscript assign */
11238 };
11239 
PyFFPrivate_Guess(PyFF_Private * self,PyObject * args)11240 static PyObject *PyFFPrivate_Guess(PyFF_Private *self, PyObject *args) {
11241     SplineFont *sf = self->sf;
11242     char *name;
11243 
11244     if ( !PyArg_ParseTuple(args,"s", &name) )
11245 return( NULL );
11246     if ( sf->private==NULL )
11247 	sf->private = calloc(1,sizeof(struct psdict));
11248 
11249     SFPrivateGuess(sf,self->fv->active_layer,sf->private,name,true);
11250 Py_RETURN( self );
11251 }
11252 
11253 static PyMethodDef FFPrivate_methods[] = {
11254     {"guess", (PyCFunction)PyFFPrivate_Guess, METH_VARARGS,
11255 	     "Given the name of an entry, guesses a default value for it." },
11256     PYMETHODDEF_EMPTY /* Sentinel */
11257 };
11258 
11259 /* ************************************************************************** */
11260 /* ************************* initializer routines *************************** */
11261 /* ************************************************************************** */
11262 
11263 static PyTypeObject PyFF_PrivateType = {
11264     PyVarObject_HEAD_INIT(NULL, 0)
11265     "fontforge.private",       /* tp_name */
11266     sizeof(PyFF_Private),      /* tp_basicsize */
11267     0,                         /* tp_itemsize */
11268     (destructor) PyFF_Private_dealloc, /* tp_dealloc */
11269     0,                         /* tp_vectorcall_offset */
11270     NULL,                      /* tp_getattr */
11271     NULL,                      /* tp_setattr */
11272     NULL,                      /* tp_compare */
11273     NULL,                      /* tp_repr */
11274     NULL,                      /* tp_as_number */
11275     NULL,                      /* tp_as_sequence */
11276     &PyFF_PrivateMapping,      /* tp_as_mapping */
11277     NULL,                      /* tp_hash */
11278     NULL,                      /* tp_call */
11279     (reprfunc) PyFFPrivate_Str,/* tp_str */
11280     NULL,                      /* tp_getattro */
11281     NULL,                      /* tp_setattro */
11282     NULL,                      /* tp_as_buffer */
11283     Py_TPFLAGS_DEFAULT,        /* tp_flags */
11284     "FontForge private dictionary", /* tp_doc */
11285     NULL,                      /* tp_traverse */
11286     NULL,                      /* tp_clear */
11287     NULL,                      /* tp_richcompare */
11288     0,                         /* tp_weaklistoffset */
11289     privateiter_new,           /* tp_iter */
11290     NULL,                      /* tp_iternext */
11291     FFPrivate_methods,         /* tp_methods */
11292     NULL,                      /* tp_members */
11293     NULL,                      /* tp_getset */
11294     NULL,                      /* tp_base */
11295     NULL,                      /* tp_dict */
11296     NULL,                      /* tp_descr_get */
11297     NULL,                      /* tp_descr_set */
11298     0,                         /* tp_dictoffset */
11299     NULL,                      /* tp_init */
11300     NULL,                      /* tp_alloc */
11301     NULL,                      /* tp_new */
11302     NULL,                      /* tp_free */
11303     NULL,                      /* tp_is_gc */
11304     NULL,                      /* tp_bases */
11305     NULL,                      /* tp_mro */
11306     NULL,                      /* tp_cache */
11307     NULL,                      /* tp_subclasses */
11308     NULL,                      /* tp_weaklist */
11309     NULL,                      /* tp_del */
11310     0,                         /* tp_version_tag */
11311 };
11312 
11313 /* ************************************************************************** */
11314 /* Font iterator type */
11315 /* ************************************************************************** */
11316 
11317 typedef struct {
11318     PyObject_HEAD
11319     SplineFont *sf;
11320     int pos;
11321     int byselection;
11322     FontViewBase *fv;
11323     struct searchdata *sv;
11324 } fontiterobject;
11325 static PyTypeObject PyFF_FontIterType;
11326 
fontiter_New(PyFF_Font * self,int bysel,struct searchdata * sv)11327 static PyObject *fontiter_New(PyFF_Font *self, int bysel, struct searchdata *sv) {
11328     fontiterobject *di;
11329 
11330     if ( CheckIfFontClosed(self) )
11331 return( NULL );
11332 
11333     di = PyObject_New(fontiterobject, &PyFF_FontIterType);
11334     if (di == NULL)
11335 return NULL;
11336     di->sf = self->fv->sf;
11337     di->fv = self->fv;
11338     di->pos = 0;
11339     di->byselection = bysel;
11340     di->sv = sv;
11341 return (PyObject *)di;
11342 }
11343 
fontiter_new_wholefont(PyObject * object)11344 static PyObject *fontiter_new_wholefont(PyObject *object) {
11345     PyFF_Font *self = (PyFF_Font*)object;
11346 return( fontiter_New(self,false,NULL) );
11347 }
11348 
fontiter_dealloc(fontiterobject * di)11349 static void fontiter_dealloc(fontiterobject *di) {
11350     PyObject_Del(di);
11351 }
11352 
fontiter_iternextkey(fontiterobject * di)11353 static PyObject *fontiter_iternextkey(fontiterobject *di) {
11354     if ( di->sv!=NULL ) {
11355 	SplineChar *sc = SDFindNext(di->sv);
11356 	if ( sc!=NULL ) {
11357 	    const char *dictfmt = "{sKsKsK}";
11358 	    PyObject *glyph, *tempdict;
11359 	    PyObject *matched;
11360 	    glyph = PySC_From_SC_I( sc );
11361 	    /* Fill matched result into the glyph.temporary atribute. */
11362 	    tempdict = PyFF_Glyph_get_temporary((PyFF_Glyph *)glyph, NULL);
11363 	    if (tempdict == NULL || !PyDict_Check(tempdict)) {
11364 		tempdict = PyDict_New();
11365 		PyFF_Glyph_set_temporary((PyFF_Glyph *)glyph, tempdict,  NULL);
11366 	    }
11367 
11368 	    matched = Py_BuildValue((char *) dictfmt,
11369 		    "findMatchedRefs", di->sv->matched_refs,
11370 		    "findMatchedContours", di->sv->matched_ss,
11371 		    "findMatchedContoursStart", di->sv->matched_ss_start
11372 		    );
11373 	    PyDict_Update(tempdict, matched);
11374 	    Py_DECREF(tempdict);
11375 	    Py_DECREF(matched);
11376 
11377 return( glyph );
11378 	}
11379     } else switch ( di->byselection ) {
11380       case 0: {		/* names of all glyphs in GID order */
11381 	SplineFont *sf = di->sf;
11382 
11383 	if (sf == NULL)
11384 return NULL;
11385 
11386 	while ( di->pos<sf->glyphcnt ) {
11387 	    if ( sf->glyphs[di->pos]!=NULL )
11388 return( Py_BuildValue("s",sf->glyphs[di->pos++]->name) );
11389 	    ++di->pos;
11390 	}
11391       break;}
11392       case 1: {		/* Encodings of selected glyphs (in encoding order) */
11393 	FontViewBase *fv = di->fv;
11394 	int enccount = fv->map->enccount;
11395 	while ( di->pos < enccount ) {
11396 	    if ( fv->selected[di->pos] )
11397 return( Py_BuildValue("i",di->pos++ ) );
11398 	    ++di->pos;
11399 	}
11400       break;}
11401       case 2: {		/* Selected glyphs in encoding order */
11402 	int gid;
11403 	FontViewBase *fv = di->fv;
11404 	int enccount = fv->map->enccount;
11405 	while ( di->pos < enccount ) {
11406 	    if ( fv->selected[di->pos] && (gid=fv->map->map[di->pos])!=-1 &&
11407 		    SCWorthOutputting(fv->sf->glyphs[gid]) ) {
11408 		++di->pos;
11409 return( PySC_From_SC_I( fv->sf->glyphs[gid] ) );
11410 	    }
11411 	    ++di->pos;
11412 	}
11413       break;}
11414       case 3: {		/* All glyphs in GID order */
11415 	FontViewBase *fv = di->fv;
11416 	int glyphcount = fv->sf->glyphcnt;
11417 	while ( di->pos < glyphcount ) {
11418 	    if ( SCWorthOutputting(fv->sf->glyphs[di->pos]) ) {
11419 return( PySC_From_SC_I( fv->sf->glyphs[di->pos++] ) );
11420 	    }
11421 	    ++di->pos;
11422 	}
11423       break;}
11424       case 4: {		/* All glyphs in encoding order */
11425 	int gid;
11426 	FontViewBase *fv = di->fv;
11427 	int enccount = fv->map->enccount;
11428 	while ( di->pos < enccount ) {
11429 	    if ( (gid=fv->map->map[di->pos])!=-1 &&
11430 		    SCWorthOutputting(fv->sf->glyphs[gid]) ) {
11431 		++di->pos;
11432 return( PySC_From_SC_I( fv->sf->glyphs[gid] ) );
11433 	    }
11434 	    ++di->pos;
11435 	}
11436       break;}
11437       default:
11438       break;
11439     }
11440 
11441 return NULL;
11442 }
11443 
11444 static PyTypeObject PyFF_FontIterType = {
11445     PyVarObject_HEAD_INIT(NULL, 0)
11446     "fontforge.font_iterator", /* tp_name */
11447     sizeof(fontiterobject),    /* tp_basicsize */
11448     0,                         /* tp_itemsize */
11449     /* methods */
11450     (destructor)fontiter_dealloc, /* tp_dealloc */
11451     0,                         /* tp_vectorcall_offset */
11452     NULL,                      /* tp_getattr */
11453     NULL,                      /* tp_setattr */
11454     NULL,                      /* tp_compare */
11455     NULL,                      /* tp_repr */
11456     NULL,                      /* tp_as_number */
11457     NULL,                      /* tp_as_sequence */
11458     NULL,                      /* tp_as_mapping */
11459     NULL,                      /* tp_hash */
11460     NULL,                      /* tp_call */
11461     NULL,                      /* tp_str */
11462     NULL,                      /* tp_getattro */
11463     NULL,                      /* tp_setattro */
11464     NULL,                      /* tp_as_buffer */
11465     Py_TPFLAGS_DEFAULT,        /* tp_flags */
11466     NULL,                      /* tp_doc */
11467     NULL,                      /* tp_traverse */
11468     NULL,                      /* tp_clear */
11469     NULL,                      /* tp_richcompare */
11470     0,                         /* tp_weaklistoffset */
11471     PyObject_SelfIter,         /* tp_iter */
11472     (iternextfunc)fontiter_iternextkey, /* tp_iternext */
11473     NULL,                      /* tp_methods */
11474     NULL,                      /* tp_members */
11475     NULL,                      /* tp_getset */
11476     NULL,                      /* tp_base */
11477     NULL,                      /* tp_dict */
11478     NULL,                      /* tp_descr_get */
11479     NULL,                      /* tp_descr_set */
11480     0,                         /* tp_dictoffset */
11481     NULL,                      /* tp_init */
11482     NULL,                      /* tp_alloc */
11483     NULL,                      /* tp_new */
11484     NULL,                      /* tp_free */
11485     NULL,                      /* tp_is_gc */
11486     NULL,                      /* tp_bases */
11487     NULL,                      /* tp_mro */
11488     NULL,                      /* tp_cache */
11489     NULL,                      /* tp_subclasses */
11490     NULL,                      /* tp_weaklist */
11491     NULL,                      /* tp_del */
11492     0,                         /* tp_version_tag */
11493 };
11494 
11495 /* ************************************************************************** */
11496 /* Font Standard Methods */
11497 /* ************************************************************************** */
11498 
PyFF_Font_dealloc(PyFF_Font * self)11499 static void PyFF_Font_dealloc(PyFF_Font *self) {
11500     if ( self->fv!=NULL ) {
11501 	if ( self->fv->python_fv_object == self )
11502 	    self->fv->python_fv_object = NULL;
11503 	self->fv = NULL;
11504     }
11505     Py_XDECREF(self->selection);
11506     Py_XDECREF(self->cvt);
11507     Py_XDECREF(self->layers);
11508     Py_XDECREF(self->private);
11509     Py_XDECREF(self->math);
11510     Py_TYPE(self)->tp_free((PyObject *) self);
11511 }
11512 
PyFF_Font_new(PyTypeObject * type,PyObject * UNUSED (args),PyObject * UNUSED (kwds))11513 static PyObject *PyFF_Font_new(PyTypeObject *type, PyObject *UNUSED(args), PyObject *UNUSED(kwds)) {
11514     PyFF_Font *self;
11515 
11516     self = (PyFF_Font *) (type->tp_alloc)(type,0);
11517     if ( self!=NULL ) {
11518 	self->fv = SFAdd(SplineFontNew(),false);
11519 	self->fv->python_fv_object = self;
11520     }
11521 return( (PyObject *) self );
11522 }
11523 
PyFFFont_close(PyFF_Font * self,PyObject * UNUSED (args))11524 static PyObject *PyFFFont_close(PyFF_Font *self, PyObject *UNUSED(args)) {
11525     FontViewBase *fv;
11526 
11527     if( CheckIfFontClosed(self) )
11528 return( NULL );
11529     fv = self->fv;
11530 
11531     if ( self->math!=NULL && self->math->sf == fv->sf )
11532 	self->math->sf = NULL;
11533 
11534     if ( self->private!=NULL && self->private->fv == fv ) {
11535 	self->private->fv = NULL;
11536 	self->private->sf = NULL;
11537     }
11538 
11539     if ( self->layers!=NULL && self->layers->sf == fv->sf )
11540 	self->layers->sf = NULL;
11541 
11542     if ( self->selection!=NULL && self->selection->fv == fv )
11543 	self->selection->fv = NULL;
11544 
11545     if ( self->cvt!=NULL && self->cvt->sf == fv->sf ) {
11546 	self->cvt->sf = NULL;
11547 	self->cvt->cvt = NULL;
11548     }
11549 
11550     fv->python_fv_object = NULL;
11551     FontViewClose(fv);
11552     self->fv = NULL;
11553 Py_RETURN_NONE;
11554 }
11555 
PyFFFont_Repr(PyFF_Font * self)11556 static PyObject *PyFFFont_Repr(PyFF_Font *self) {
11557     PyObject *ret;
11558     char prefix[256];
11559 #ifdef DEBUG
11560     snprintf(prefix,sizeof(prefix), "<%s at 0x%p fv=0x%p sf=0x%p",
11561 	     Py_TYPENAME(self),
11562 	     self,
11563 	     self->fv,
11564 	     (self->fv ? self->fv->sf : 0) );
11565 #else
11566     snprintf(prefix,sizeof(prefix), "<%s at 0x%p",
11567 	     Py_TYPENAME(self), self );
11568 #endif
11569     if ( self->fv==NULL )
11570 	ret = PyUnicode_FromFormat("%s CLOSED>",prefix);
11571     else
11572 	ret = PyUnicode_FromFormat("%s \"%s\">",prefix,self->fv->sf->fontname);
11573     return( ret );
11574 }
11575 
PyFFFont_Str(PyFF_Font * self)11576 static PyObject *PyFFFont_Str(PyFF_Font *self) {
11577 return( PyUnicode_FromFormat( "<Font: %s>",
11578 			    IsFontClosed(self) ? "<closed>" : self->fv->sf->fontname ));
11579 }
11580 
11581 /* ************************************************************************** */
11582 /* sfnt 'name' table stuff */
11583 /* ************************************************************************** */
11584 
11585 
sfntnametuple(int lang,int strid,char * name)11586 static PyObject *sfntnametuple(int lang,int strid,char *name) {
11587     PyObject *tuple;
11588     int i;
11589 
11590     tuple = PyTuple_New(3);
11591 
11592     PyTuple_SetItem(tuple,2,Py_BuildValue("s", name));
11593 
11594     for ( i=0; sfnt_name_mslangs[i].name!=NULL ; ++i )
11595 	if ( sfnt_name_mslangs[i].flag == lang )
11596     break;
11597     if ( sfnt_name_mslangs[i].flag == lang )
11598 	PyTuple_SetItem(tuple,0,Py_BuildValue("s", sfnt_name_mslangs[i].name));
11599     else
11600 	PyTuple_SetItem(tuple,0,Py_BuildValue("i", lang));
11601 
11602     for ( i=0; sfnt_name_str_ids[i].name!=NULL ; ++i )
11603 	if ( sfnt_name_str_ids[i].flag == strid )
11604     break;
11605     if ( sfnt_name_str_ids[i].flag == strid )
11606 	PyTuple_SetItem(tuple,1,Py_BuildValue("s", sfnt_name_str_ids[i].name));
11607     else
11608 	PyTuple_SetItem(tuple,1,Py_BuildValue("i", strid));
11609 return( tuple );
11610 }
11611 
SetSFNTName(SplineFont * sf,PyObject * tuple,struct ttflangname * english)11612 static int SetSFNTName(SplineFont *sf,PyObject *tuple,struct ttflangname *english) {
11613     const char *lang_str, *strid_str, *string;
11614     int lang, strid;
11615     PyObject *val;
11616     struct ttflangname *names;
11617 
11618     if ( PySequence_Size(tuple)!=3 ) {
11619 	PyErr_Format(PyExc_TypeError, "sfnt_name must be a tuples of three strings" );
11620 return(0);
11621     }
11622 
11623     val = PySequence_GetItem(tuple,0);
11624     if ( PyUnicode_Check(val) ) {
11625         if ((lang_str = PyUnicode_AsUTF8(val)) != NULL) {
11626             lang = FlagsFromString(lang_str,sfnt_name_mslangs,"language");
11627         }
11628         if (lang_str == NULL || lang == FLAG_UNKNOWN) {
11629             Py_DECREF(val);
11630             return 0;
11631         }
11632     } else if ( PyLong_Check(val))
11633 	lang = PyLong_AsLong(val);
11634     else {
11635 	Py_XDECREF(val);
11636 	PyErr_Format(PyExc_TypeError, "Language must be a string or an integer" );
11637 return( 0 );
11638     }
11639     Py_DECREF(val);
11640 
11641     val = PySequence_GetItem(tuple,1);
11642     if ( PyUnicode_Check(val) ) {
11643         if ((strid_str = PyUnicode_AsUTF8(val)) != NULL) {
11644             strid = FlagsFromString(strid_str,sfnt_name_str_ids,"string id");
11645         }
11646         if (strid_str == NULL || strid == FLAG_UNKNOWN) {
11647             Py_DECREF(val);
11648             return 0;
11649         }
11650     } else if ( PyLong_Check(val))
11651 	strid = PyLong_AsLong(val);
11652     else {
11653 	Py_XDECREF(val);
11654 	PyErr_Format(PyExc_TypeError, "String-id must be a string or an integer" );
11655 return( 0 );
11656     }
11657     Py_DECREF(val);
11658 
11659     for ( names=sf->names; names!=NULL; names=names->next )
11660 	if ( names->lang==lang )
11661     break;
11662 
11663     val = PySequence_GetItem(tuple, 2);
11664     if ( val==Py_None ) {
11665 	if ( names!=NULL ) {
11666 	    free( names->names[strid] );
11667 	    names->names[strid] = NULL;
11668 	}
11669 	Py_DECREF(val);
11670 return( 1 );
11671     }
11672 
11673     string = PyUnicode_AsUTF8(val);
11674     if ( string==NULL ) {
11675 	Py_XDECREF(val);
11676 return( 0 );
11677     }
11678     if ( lang==0x409 && english!=NULL && english->names[strid]!=NULL &&
11679          strcmp(string,english->names[strid])==0 ) {
11680 	Py_DECREF(val);
11681 return( 1 );	/* If they set it to the default, there's nothing to do */
11682     }
11683 
11684     if ( names==NULL ) {
11685 	names = chunkalloc(sizeof( struct ttflangname ));
11686 	names->lang = lang;
11687 	names->next = sf->names;
11688 	sf->names = names;
11689     }
11690     free(names->names[strid]);
11691     names->names[strid] = copy( string );
11692     Py_DECREF(val);
11693 return( 1 );
11694 }
11695 
11696 /* ************************************************************************** */
11697 /* Font getters/setters */
11698 /* ************************************************************************** */
11699 
PyFF_Font_get_sfntnames(PyFF_Font * self,void * UNUSED (closure))11700 static PyObject *PyFF_Font_get_sfntnames(PyFF_Font *self, void *UNUSED(closure)) {
11701     struct ttflangname *names, *english;
11702     int cnt, i;
11703     PyObject *tuple;
11704     struct ttflangname dummy;
11705     SplineFont *sf;
11706 
11707     if ( CheckIfFontClosed(self) )
11708 return( NULL );
11709 
11710     sf = self->fv->sf;
11711     memset(&dummy,0,sizeof(dummy));
11712     DefaultTTFEnglishNames(&dummy, sf);
11713 
11714     cnt = 0;
11715     for ( english = sf->names; english!=NULL; english=english->next )
11716 	if ( english->lang==0x409 )
11717     break;
11718     for ( i=0; i<ttf_namemax; ++i ) {
11719 	if ( (english!=NULL && english->names[i]!=NULL ) || dummy.names[i]!=NULL )
11720 	    ++cnt;
11721     }
11722     for ( names = sf->names; names!=NULL; names=names->next ) if ( names!=english ) {
11723 	for ( i=0; i<ttf_namemax; ++i ) {
11724 	    if ( names->names[i]!=NULL )
11725 		++cnt;
11726 	}
11727     }
11728     tuple = PyTuple_New(cnt);
11729     cnt = 0;
11730     for ( i=0; i<ttf_namemax; ++i ) {
11731 	char *nm = (english!=NULL && english->names[i]!=NULL ) ? english->names[i] : dummy.names[i];
11732 	if ( nm!=NULL )
11733 	    PyTuple_SetItem(tuple,cnt++,sfntnametuple(0x409,i,nm));
11734     }
11735     for ( names = sf->names; names!=NULL; names=names->next ) if ( names!=english ) {
11736 	for ( i=0; i<ttf_namemax; ++i ) {
11737 	    if ( names->names[i]!=NULL )
11738 		PyTuple_SetItem(tuple,cnt++,sfntnametuple(names->lang,i,names->names[i]));
11739 	}
11740     }
11741 
11742     for ( i=0; i<ttf_namemax; ++i )
11743 	free( dummy.names[i]);
11744 
11745 return( tuple );
11746 }
11747 
PyFF_Font_set_sfntnames(PyFF_Font * self,PyObject * value,void * UNUSED (closure))11748 static int PyFF_Font_set_sfntnames(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
11749     SplineFont *sf;
11750     struct ttflangname *names;
11751     struct ttflangname dummy;
11752     int i;
11753 
11754     if ( CheckIfFontClosed(self) )
11755 return(-1);
11756 
11757     sf = self->fv->sf;
11758     if ( !PySequence_Check(value)) {
11759 	PyErr_Format(PyExc_TypeError, "Value must be a tuple" );
11760 return(-1);
11761     }
11762 
11763     memset(&dummy,0,sizeof(dummy));
11764     DefaultTTFEnglishNames(&dummy, sf);
11765 
11766     for ( names = sf->names; names!=NULL; names=names->next ) {
11767 	for ( i=0; i<ttf_namemax; ++i ) {
11768 	    free(names->names[i]);
11769 	    names->names[i] = NULL;
11770 	}
11771     }
11772     for ( i=PySequence_Size(value)-1; i>=0; --i )
11773 	if ( !SetSFNTName(sf,PySequence_GetItem(value,i),&dummy) )
11774 return( -1 );
11775 
11776     for ( i=0; i<ttf_namemax; ++i )
11777 	free( dummy.names[i]);
11778 
11779 return( 0 );
11780 }
11781 
PyFF_Font_get_bitmapSizes(PyFF_Font * self,void * UNUSED (closure))11782 static PyObject *PyFF_Font_get_bitmapSizes(PyFF_Font *self, void *UNUSED(closure)) {
11783     PyObject *tuple;
11784     int cnt;
11785     SplineFont *sf;
11786     BDFFont *bdf;
11787 
11788     if ( CheckIfFontClosed(self) )
11789 return( NULL );
11790 
11791     sf = self->fv->sf;
11792     for ( cnt=0, bdf=sf->bitmaps; bdf!=NULL; bdf=bdf->next, ++cnt );
11793 
11794     tuple = PyTuple_New(cnt);
11795     for ( cnt=0, bdf=sf->bitmaps; bdf!=NULL; bdf=bdf->next, ++cnt )
11796 	PyTuple_SetItem(tuple,cnt,Py_BuildValue("i",
11797 		bdf->clut==NULL ? bdf->pixelsize :
11798 				(bdf->pixelsize | (BDFDepth(bdf)<<16)) ));
11799 
11800 return( tuple );
11801 }
11802 
bitmapper(PyFF_Font * self,PyObject * value,int isavail)11803 static int bitmapper(PyFF_Font *self,PyObject *value,int isavail) {
11804     int cnt, i;
11805     int *sizes;
11806 
11807     if ( CheckIfFontClosed(self) )
11808 return(-1);
11809 
11810     cnt = PyTuple_Size(value);
11811     if ( PyErr_Occurred())
11812 return( -1 );
11813     sizes = malloc((cnt+1)*sizeof(int));
11814     for ( i=0; i<cnt; ++i ) {
11815 	if ( !PyArg_ParseTuple(PyTuple_GetItem(value,i),"i", &sizes[i])) {
11816 	    free(sizes);
11817 return( -1 );
11818 	}
11819 	if ( (sizes[i]>>16)==0 )
11820 	    sizes[i] |= 0x10000;
11821     }
11822     sizes[i] = 0;
11823 
11824     if ( !BitmapControl(self->fv,sizes,isavail,false) ) {
11825 	free(sizes);
11826 	PyErr_Format(PyExc_EnvironmentError, "Bitmap operation failed");
11827 return( -1 );
11828     }
11829     free(sizes);
11830 return( 0 );
11831 }
11832 
PyFF_Font_set_bitmapSizes(PyFF_Font * self,PyObject * value,void * UNUSED (closure))11833 static int PyFF_Font_set_bitmapSizes(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
11834 return( bitmapper(self,value,true));
11835 }
11836 
11837 /* GASP (grid-fitting and scan procedure) flags */
11838 static struct flaglist gaspflags[] = {
11839     { "gridfit",		1 },
11840     { "antialias",		2 },
11841     { "symmetric-smoothing",	8 },
11842     { "gridfit+smoothing",	4 },
11843     FLAGLIST_EMPTY /* Sentinel */
11844 };
11845 
PyFF_Font_get_gasp(PyFF_Font * self,void * UNUSED (closure))11846 static PyObject *PyFF_Font_get_gasp(PyFF_Font *self, void *UNUSED(closure)) {
11847     PyObject *tuple, *flagstuple;
11848     int i, j, cnt;
11849     SplineFont *sf;
11850 
11851     if ( CheckIfFontClosed(self) )
11852 return(NULL);
11853 
11854     sf = self->fv->sf;
11855     tuple = PyTuple_New(sf->gasp_cnt);
11856 
11857     for ( i=0; i<sf->gasp_cnt; ++i ) {
11858 	for ( j=cnt=0; gaspflags[j].name!=NULL; ++j )
11859 	    if ( sf->gasp[i].flags & gaspflags[j].flag )
11860 		++cnt;
11861 	flagstuple = PyTuple_New(cnt);
11862 	for ( j=cnt=0; gaspflags[j].name!=NULL; ++j )
11863 	    if ( sf->gasp[i].flags & gaspflags[j].flag )
11864 		PyTuple_SetItem(flagstuple,cnt++,Py_BuildValue("s", gaspflags[j].name));
11865 	PyTuple_SetItem(tuple,i,Py_BuildValue("iO",sf->gasp[i].ppem,flagstuple));
11866     }
11867 
11868 return( tuple );
11869 }
11870 
PyFF_Font_set_gasp(PyFF_Font * self,PyObject * value,void * UNUSED (closure))11871 static int PyFF_Font_set_gasp(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
11872     SplineFont *sf;
11873     int cnt, i, flag;
11874     struct gasp *gasp;
11875     PyObject *flags;
11876 
11877     if ( CheckIfFontClosed(self) )
11878 return(-1);
11879 
11880     sf = self->fv->sf;
11881     cnt = PyTuple_Size(value);
11882     if ( PyErr_Occurred())
11883 return( -1 );
11884     if ( cnt==0 )
11885 	gasp = NULL;
11886     else {
11887 	gasp = malloc(cnt*sizeof(struct gasp));
11888 	for ( i=0; i<cnt; ++i ) {
11889 	    if ( !PyArg_ParseTuple(PyTuple_GetItem(value,i),"HO",
11890 				   &gasp[i].ppem, &flags )) {
11891 		free(gasp);
11892 return( -1 );
11893 	    }
11894 	    flag = FlagsFromTuple(flags,gaspflags,"gasp flag");
11895 	    if ( flag==FLAG_UNKNOWN ) {
11896 		free(gasp);
11897 return( -1 );
11898 	    }
11899 	    gasp[i].flags = flag;
11900 	}
11901     }
11902     free(sf->gasp);
11903     sf->gasp = gasp;
11904     sf->gasp_cnt = cnt;
11905 return( 0 );
11906 }
11907 
PyFF_Font_get_lookups(PyFF_Font * self,void * UNUSED (closure),int isgpos)11908 static PyObject *PyFF_Font_get_lookups(PyFF_Font *self, void *UNUSED(closure), int isgpos) {
11909     PyObject *tuple;
11910     OTLookup *otl;
11911     int cnt;
11912     SplineFont *sf;
11913 
11914     if ( CheckIfFontClosed(self) )
11915 return(NULL);
11916 
11917     sf = self->fv->sf;
11918     cnt = 0;
11919     for ( otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next )
11920 	++cnt;
11921 
11922     tuple = PyTuple_New(cnt);
11923 
11924     cnt = 0;
11925     for ( otl=isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl!=NULL; otl=otl->next )
11926 	PyTuple_SetItem(tuple,cnt++,Py_BuildValue("s",otl->lookup_name));
11927 
11928 return( tuple );
11929 }
11930 
PyFF_Font_get_gpos_lookups(PyFF_Font * self,void * closure)11931 static PyObject *PyFF_Font_get_gpos_lookups(PyFF_Font *self, void *closure) {
11932 return( PyFF_Font_get_lookups(self,closure,true));
11933 }
11934 
PyFF_Font_get_gsub_lookups(PyFF_Font * self,void * closure)11935 static PyObject *PyFF_Font_get_gsub_lookups(PyFF_Font *self, void *closure) {
11936 return( PyFF_Font_get_lookups(self,closure,false));
11937 }
11938 
PyFF_Font_get_math(PyFF_Font * self,void * UNUSED (closure))11939 static PyObject *PyFF_Font_get_math(PyFF_Font *self, void *UNUSED(closure)) {
11940     PyFF_Math *math;
11941 
11942     if ( CheckIfFontClosed(self) )
11943 return(NULL);
11944 
11945     if ( self->math!=NULL )
11946 Py_RETURN( self->math );
11947 
11948     math = (PyFF_Math *) PyObject_New(PyFF_Math, &PyFF_MathType);
11949     if (math == NULL)
11950 return NULL;
11951     math->sf = self->fv->sf;
11952     self->math = math;
11953 Py_RETURN( self->math );
11954 }
11955 
PyFF_Font_get_private(PyFF_Font * self,void * UNUSED (closure))11956 static PyObject *PyFF_Font_get_private(PyFF_Font *self, void *UNUSED(closure)) {
11957     PyFF_Private *private;
11958 
11959     if ( CheckIfFontClosed(self) )
11960 return(NULL);
11961 
11962     if ( self->private!=NULL )
11963 Py_RETURN( self->private );
11964     private = (PyFF_Private *) PyObject_New(PyFF_Private, &PyFF_PrivateType);
11965     if (private == NULL)
11966 return NULL;
11967     private->sf = self->fv->sf;
11968     private->fv = self->fv;
11969     self->private = private;
11970 Py_RETURN( self->private );
11971 }
11972 
PyFF_Font_get_layers(PyFF_Font * self,void * UNUSED (closure))11973 static PyObject *PyFF_Font_get_layers(PyFF_Font *self, void *UNUSED(closure)) {
11974     PyFF_LayerInfoArray *layers;
11975 
11976     if ( CheckIfFontClosed(self) )
11977 return(NULL);
11978 
11979     if ( self->layers!=NULL )
11980 Py_RETURN( self->layers );
11981 
11982     layers = (PyFF_LayerInfoArray *) PyObject_New(PyFF_LayerInfoArray, &PyFF_LayerInfoArrayType);
11983     if (layers == NULL)
11984 return NULL;
11985     layers->sf = self->fv->sf;
11986     self->layers = layers;
11987 Py_RETURN( self->layers );
11988 }
11989 
PyFF_Font_get_layer_cnt(PyFF_Font * self,void * UNUSED (closure))11990 static PyObject *PyFF_Font_get_layer_cnt(PyFF_Font *self, void *UNUSED(closure)) {
11991     if ( CheckIfFontClosed(self) )
11992 return(NULL);
11993 return( Py_BuildValue("i", self->fv->sf->layer_cnt ));
11994 }
11995 
PyFF_Font_get_activeLayer(PyFF_Font * self,void * UNUSED (closure))11996 static PyObject *PyFF_Font_get_activeLayer(PyFF_Font *self, void *UNUSED(closure)) {
11997     if ( CheckIfFontClosed(self) )
11998 return(NULL);
11999 return( Py_BuildValue("i", self->fv->active_layer ));
12000 }
12001 
PyFF_Font_set_activeLayer(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12002 static int PyFF_Font_set_activeLayer(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12003     int layer;
12004 
12005     if ( CheckIfFontClosed(self) )
12006 return(-1);
12007 
12008     if ( PyLong_Check(value) )
12009 	layer = PyLong_AsLong(value);
12010     else if ( PyUnicode_Check(value)) {
12011 	const char *name = PyUnicode_AsUTF8(value);
12012 	if (name == NULL) {
12013 	    return -1;
12014 	}
12015 	layer = SFFindLayerIndexByName(self->fv->sf,name);
12016 	if ( layer<0 )
12017 return( -1 );
12018     } else {
12019         return -1;
12020     }
12021     if ( layer<0 || layer>=self->fv->sf->layer_cnt ) {
12022 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
12023 return( -1 );
12024     }
12025     self->fv->active_layer = layer;
12026 return( 0 );
12027 }
12028 
PyFF_Font_get_selection(PyFF_Font * self,void * UNUSED (closure))12029 static PyObject *PyFF_Font_get_selection(PyFF_Font *self, void *UNUSED(closure)) {
12030 return( PyFFSelection_new(self));
12031 }
12032 
PyFF_Font_set_selection(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12033 static int PyFF_Font_set_selection(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12034     PyFF_Selection *sel = (PyFF_Selection *) value;
12035     int i, len2;
12036     int is_sel;
12037     FontViewBase *fv;
12038 
12039     if ( CheckIfFontClosed(self) )
12040 return(-1);
12041 
12042     fv = self->fv;
12043     if ( PyType_IsSubtype(&PyFF_SelectionType, Py_TYPE(value)) ) {
12044 	len2 = PyFFSelection_Length(value);
12045 	is_sel = true;
12046     } else if ( PySequence_Check(value)) {
12047 	is_sel = false;
12048 	len2 = PySequence_Size(value);
12049     } else {
12050 	PyErr_Format(PyExc_TypeError, "The value must be either another selection or a tuple of integers");
12051 return( -1 );
12052     }
12053 
12054     if ( len2>=fv->map->enccount ) {
12055 	PyErr_Format(PyExc_TypeError, "Too much data");
12056 return( -1 );
12057     }
12058     if ( is_sel ) {
12059 	if ( len2!=0 )
12060 	    memcpy(fv->selected,sel->fv->selected,len2 );
12061     } else {
12062 	for( i=0; i<len2; ++i ) {
12063 	    int val;
12064 	    PyObject *obj = PySequence_GetItem(value,i);
12065 	    if ( obj==Py_True )
12066 		val = 1;
12067 	    else if ( obj==Py_False )
12068 		val = 0;
12069 	    else {
12070 		val = PyLong_AsLong(obj);
12071 		if ( PyErr_Occurred())
12072 return( -1 );
12073 	    }
12074 	    fv->selected[i] = val;
12075 	}
12076     }
12077 return( 0 );
12078 }
12079 
PyFF_Font_get_cvt(PyFF_Font * self,void * UNUSED (closure))12080 static PyObject *PyFF_Font_get_cvt(PyFF_Font *self, void *UNUSED(closure)) {
12081 return( PyFFCvt_new(self));
12082 }
12083 
PyFF_Font_set_cvt(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12084 static int PyFF_Font_set_cvt(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12085     PyFF_Cvt *c2 = (PyFF_Cvt *) value;
12086     size_t i, len2;
12087     int is_cvt2;
12088     SplineFont *sf;
12089     struct ttf_table *cvt;
12090 
12091     if ( CheckIfFontClosed(self) )
12092 return(-1);
12093 
12094     sf = self->fv->sf;
12095     if ( PyType_IsSubtype(&PyFF_CvtType, Py_TYPE(value)) ) {
12096 	len2 = PyFFCvt_Length(value);
12097 	is_cvt2 = true;
12098     } else if ( PySequence_Check(value)) {
12099 	is_cvt2 = false;
12100 	len2 = PySequence_Size(value);
12101     } else {
12102 	PyErr_Format(PyExc_TypeError, "The value must be either another cvt or a tuple of integers");
12103 return( -1 );
12104     }
12105 
12106     cvt = SFFindTable(sf,CHR('c','v','t',' '));
12107     if ( cvt==NULL )
12108 	cvt = BuildCvt(sf,len2*2);
12109     if ( len2*2>=cvt->maxlen )
12110 	cvt->data = realloc(cvt->data,cvt->maxlen = sizeof(uint16)*len2+10 );
12111     if ( is_cvt2 ) {
12112 	if ( len2!=0 )
12113 	    memcpy(cvt->data,c2->cvt->data,2*len2 );
12114     } else {
12115 	for( i=0; i<len2; ++i ) {
12116 	    memputshort(cvt->data,2*i,PyLong_AsLong(PySequence_GetItem(value,i)));
12117 	    if ( PyErr_Occurred())
12118 return( -1 );
12119 	}
12120     }
12121     cvt->len = 2*len2;
12122 return( 0 );
12123 }
12124 
PyFF_Font_get_temporary(PyFF_Font * self,void * UNUSED (closure))12125 static PyObject *PyFF_Font_get_temporary(PyFF_Font *self, void *UNUSED(closure)) {
12126     SplineFont *sf;
12127 
12128     if ( CheckIfFontClosed(self) )
12129 return(NULL);
12130 
12131     sf = self->fv->sf;
12132     if ( sf->python_temporary==NULL )
12133 Py_RETURN_NONE;
12134     Py_INCREF( (PyObject *) (sf->python_temporary) );
12135 return( sf->python_temporary );
12136 }
12137 
PyFF_Font_set_temporary(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12138 static int PyFF_Font_set_temporary(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12139     SplineFont *sf;
12140     PyObject *old;
12141 
12142     if ( CheckIfFontClosed(self) )
12143 return(-1);
12144 
12145     sf = self->fv->sf;
12146     old = sf->python_temporary;
12147     /* I'd rather not store None, because C routines don't understand it */
12148     /*  and they occasionally need to know whether there is something real */
12149     /*  in this field. */
12150     if ( value==Py_None )
12151 	value = NULL;
12152     Py_XINCREF(value);
12153     sf->python_temporary = value;
12154     Py_XDECREF(old);
12155 return( 0 );
12156 }
12157 
PyFF_Font_get_persistent(PyFF_Font * self,void * UNUSED (closure))12158 static PyObject *PyFF_Font_get_persistent(PyFF_Font *self, void *UNUSED(closure)) {
12159     SplineFont *sf;
12160 
12161     if ( CheckIfFontClosed(self) )
12162 return(NULL);
12163 
12164     sf = self->fv->sf;
12165     if ( sf->python_persistent==NULL )
12166 Py_RETURN_NONE;
12167     Py_INCREF( (PyObject *) (sf->python_persistent) );
12168 return( sf->python_persistent );
12169 }
12170 
PyFF_Font_set_persistent(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12171 static int PyFF_Font_set_persistent(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12172     SplineFont *sf;
12173     PyObject *old;
12174 
12175     if ( CheckIfFontClosed(self) )
12176 return(-1);
12177 
12178     sf = self->fv->sf;
12179     old = sf->python_persistent;
12180     /* I'd rather not store None, because C routines don't understand it */
12181     /*  and they occasionally need to know whether there is something real */
12182     /*  in this field. */
12183     if ( value==Py_None )
12184 	value = NULL;
12185     Py_XINCREF(value);
12186     sf->python_persistent = value;
12187     Py_XDECREF(old);
12188 return( 0 );
12189 }
12190 
_PyFF_Font_set_str_null(SplineFont * sf,PyObject * value,const char * str,int offset)12191 static int _PyFF_Font_set_str_null(SplineFont *sf,PyObject *value,
12192 	const char *str,int offset) {
12193     char *newv, **oldpos;
12194     PyObject *temp;
12195 
12196     if ( value==NULL ) {
12197 	PyErr_Format(PyExc_TypeError, "Cannot delete the %s", str);
12198 return( -1 );
12199     }
12200     newv = (value == Py_None) ? NULL : copy(PyUnicode_AsUTF8(value));
12201 
12202     if ( newv==NULL && value!=Py_None )
12203 return( -1 );
12204     oldpos = (char **) (((char *) sf) + offset );
12205     free( *oldpos );
12206     *oldpos = newv;
12207 return( 0 );
12208 }
12209 
PyFF_Font_set_str_null(PyFF_Font * self,PyObject * value,const char * str,int offset)12210 static int PyFF_Font_set_str_null(PyFF_Font *self,PyObject *value,
12211 				  const char *str,int offset) {
12212     if ( CheckIfFontClosed(self) )
12213 return(-1);
12214 return( _PyFF_Font_set_str_null(self->fv->sf,value,str,offset));
12215 }
12216 
PyFF_Font_set_cidstr_null(PyFF_Font * self,PyObject * value,const char * str,int offset)12217 static int PyFF_Font_set_cidstr_null(PyFF_Font *self,PyObject *value,
12218 				     const char *str,int offset) {
12219     if ( CheckIfFontClosed(self) )
12220 return(-1);
12221     if ( self->fv->cidmaster==NULL ) {
12222 	PyErr_Format(PyExc_EnvironmentError, "Not a cid-keyed font");
12223 return( -1 );
12224     }
12225 return( _PyFF_Font_set_str_null(self->fv->cidmaster,value,str,offset));
12226 }
12227 
PyFF_Font_set_str(PyFF_Font * self,PyObject * value,char * str,int offset)12228 static int PyFF_Font_set_str(PyFF_Font *self,PyObject *value,
12229 			     char *str,int offset) {
12230     char *newv, **oldpos;
12231 
12232     if ( CheckIfFontClosed(self) )
12233 return(-1);
12234     if ( value==NULL ) {
12235 	PyErr_Format(PyExc_TypeError, "Cannot delete the %s", str);
12236 return( -1 );
12237     }
12238 
12239     newv = copy(PyUnicode_AsUTF8(value));
12240 
12241     if ( newv==NULL )
12242 return( -1 );
12243     oldpos = (char **) (((char *) (self->fv->sf)) + offset );
12244     free( *oldpos );
12245     *oldpos = newv;
12246 return( 0 );
12247 }
12248 
_PyFF_Font_set_real(SplineFont * sf,PyObject * value,const char * str,int offset)12249 static int _PyFF_Font_set_real(SplineFont *sf,PyObject *value,
12250 			       const char *str,int offset) {
12251     double temp;
12252 
12253     if ( value==NULL ) {
12254 	PyErr_Format(PyExc_TypeError, "Cannot delete the %s", str);
12255 return( -1 );
12256     }
12257     temp = PyFloat_AsDouble(value);
12258     if ( PyErr_Occurred()!=NULL )
12259 return( -1 );
12260     * (real *) (((char *) sf) + offset ) = temp;
12261 return( 0 );
12262 }
12263 
PyFF_Font_set_real(PyFF_Font * self,PyObject * value,const char * str,int offset)12264 static int PyFF_Font_set_real(PyFF_Font *self,PyObject *value,
12265 			      const char *str,int offset) {
12266     if ( CheckIfFontClosed(self) )
12267 return(-1);
12268 return( _PyFF_Font_set_real( self->fv->sf,value,str,offset));
12269 }
12270 
PyFF_Font_set_cidreal(PyFF_Font * self,PyObject * value,const char * str,int offset)12271 static int PyFF_Font_set_cidreal(PyFF_Font *self,PyObject *value,
12272 				 const char *str,int offset) {
12273     if ( CheckIfFontClosed(self) )
12274 return(-1);
12275     if ( self->fv->cidmaster==NULL ) {
12276 	PyErr_Format(PyExc_EnvironmentError, "Not a cid-keyed font");
12277 return( -1 );
12278     }
12279 return( _PyFF_Font_set_real( self->fv->cidmaster,value,str,offset));
12280 }
12281 
_PyFF_Font_set_int(SplineFont * sf,PyObject * value,const char * str,int offset)12282 static int _PyFF_Font_set_int(SplineFont *sf,PyObject *value,
12283 	const char *str,int offset) {
12284     long temp;
12285 
12286     if ( value==NULL ) {
12287 	PyErr_Format(PyExc_TypeError, "Cannot delete the %s", str);
12288 return( -1 );
12289     }
12290     temp = PyLong_AsLong(value);
12291     if ( PyErr_Occurred()!=NULL )
12292 return( -1 );
12293     * (int *) (((char *) sf) + offset ) = temp;
12294 return( 0 );
12295 }
12296 
PyFF_Font_set_int(PyFF_Font * self,PyObject * value,const char * str,int offset)12297 static int PyFF_Font_set_int(PyFF_Font *self,PyObject *value,
12298 			     const char *str,int offset) {
12299     if ( CheckIfFontClosed(self) )
12300 return(-1);
12301 return( _PyFF_Font_set_int( self->fv->sf,value,str,offset));
12302 }
12303 
PyFF_Font_set_cidint(PyFF_Font * self,PyObject * value,const char * str,int offset)12304 static int PyFF_Font_set_cidint(PyFF_Font *self,PyObject *value,
12305 				const char *str,int offset) {
12306     if ( CheckIfFontClosed(self) )
12307 return(-1);
12308     if ( self->fv->cidmaster==NULL ) {
12309 	PyErr_Format(PyExc_EnvironmentError, "Not a cid-keyed font");
12310 return( -1 );
12311     }
12312 return( _PyFF_Font_set_int( self->fv->cidmaster,value,str,offset));
12313 }
12314 
PyFF_Font_set_int2(PyFF_Font * self,PyObject * value,const char * str,int offset)12315 static int PyFF_Font_set_int2(PyFF_Font *self,PyObject *value,
12316 			      const char *str,int offset) {
12317     long temp;
12318     if ( CheckIfFontClosed(self) )
12319 return(-1);
12320     if ( value==NULL ) {
12321 	PyErr_Format(PyExc_TypeError, "Cannot delete the %s", str);
12322 return( -1 );
12323     }
12324     temp = PyLong_AsLong(value);
12325     if ( PyErr_Occurred()!=NULL )
12326 return( -1 );
12327     * (int16 *) (((char *) (self->fv->sf)) + offset ) = temp;
12328 return( 0 );
12329 }
12330 
PyFF_Font_get_texparams(PyFF_Font * self,void * UNUSED (closure))12331 static PyObject *PyFF_Font_get_texparams(PyFF_Font *self, void *UNUSED(closure)) {
12332     SplineFont *sf;
12333     int i, em;
12334     PyObject *tuple;
12335     double val;
12336 
12337     if ( CheckIfFontClosed(self) )
12338 return(NULL);
12339 
12340     sf = self->fv->sf;
12341     em = sf->ascent+sf->descent;
12342     tuple = PyTuple_New(23);
12343 
12344     if ( sf->texdata.type==tex_text )
12345 	PyTuple_SetItem(tuple,0,Py_BuildValue("s", "text"));
12346     else if ( sf->texdata.type==tex_math )
12347 	PyTuple_SetItem(tuple,0,Py_BuildValue("s", "mathsym"));
12348     else if ( sf->texdata.type==tex_mathext )
12349 	PyTuple_SetItem(tuple,0,Py_BuildValue("s", "mathext"));
12350     else if ( sf->texdata.type==tex_unset ) {
12351 	PyTuple_SetItem(tuple,0,Py_BuildValue("s", "unset"));
12352 	TeXDefaultParams(sf);
12353     }
12354 
12355     for ( i=1; i<23; i++ ) {
12356 	val = rint( (double) sf->texdata.params[i-1] * em / (1<<20) );
12357 	PyTuple_SetItem(tuple,i,Py_BuildValue( "d", val ));
12358     }
12359 
12360 return( tuple );
12361 }
12362 
12363 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12364 #define ff_gs_str(name) \
12365 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12366     if ( CheckIfFontClosed(self) ) return(NULL);	\
12367 return( Py_BuildValue("s", self->fv->sf->name ));	\
12368 }							\
12369 							\
12370 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12371     if(CheckIfFontClosed(self)) return(-1);				\
12372 return( PyFF_Font_set_str(self,value,(char *)#name,offsetof(SplineFont,name)) ); \
12373 }
12374 
12375 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12376 #define ff_gs_strnull(name) \
12377 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12378     if ( CheckIfFontClosed(self) ) return(NULL);	\
12379     if ( self->fv->sf->name==NULL )			\
12380 Py_RETURN_NONE;						\
12381     else						\
12382 return( Py_BuildValue("s", self->fv->sf->name ));	\
12383 }							\
12384 							\
12385 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12386     if ( CheckIfFontClosed(self) ) return(-1);				     \
12387 return( PyFF_Font_set_str_null(self,value,#name,offsetof(SplineFont,name)) );\
12388 }
12389 
12390 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12391 #define ff_gs_cidstrnull(name) \
12392 static PyObject *PyFF_Font_get_cid##name(PyFF_Font *self, void *UNUSED(closure)) { \
12393     if ( CheckIfFontClosed(self) ) return(NULL);	\
12394     if ( self->fv->cidmaster==NULL )			\
12395 Py_RETURN_NONE;						\
12396     if ( self->fv->cidmaster->name==NULL )		\
12397 Py_RETURN_NONE;						\
12398     else						\
12399 return( Py_BuildValue("s", self->fv->cidmaster->name )); \
12400 }							\
12401 							\
12402 static int PyFF_Font_set_cid##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12403     if ( CheckIfFontClosed(self) ) return(-1);					      \
12404 return( PyFF_Font_set_cidstr_null(self,value,"cid" #name,offsetof(SplineFont,name)) );\
12405 }
12406 
12407 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12408 #define ff_gs_real(name) \
12409 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12410     if ( CheckIfFontClosed(self) ) return(NULL);	\
12411 return( Py_BuildValue("d", self->fv->sf->name ));	\
12412 }							\
12413 							\
12414 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12415     if ( CheckIfFontClosed(self) ) return(-1);				 \
12416 return( PyFF_Font_set_real(self,value,#name,offsetof(SplineFont,name)) );\
12417 }
12418 
12419 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12420 #define ff_gs_cidreal(name) \
12421 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12422     if ( CheckIfFontClosed(self) ) return (NULL);	\
12423     if ( self->fv->cidmaster==NULL )			\
12424 Py_RETURN_NONE;						\
12425 return( Py_BuildValue("d", (double)self->fv->cidmaster->name ));	\
12426 }							\
12427 							\
12428 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12429     if ( CheckIfFontClosed(self) ) return (-1);				    \
12430 return( PyFF_Font_set_cidreal(self,value,#name,offsetof(SplineFont,name)) );\
12431 }
12432 
12433 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12434 #define ff_gs_int(name) \
12435 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12436     if ( CheckIfFontClosed(self) ) return (NULL);	\
12437 return( Py_BuildValue("i", self->fv->sf->name ));	\
12438 }							\
12439 							\
12440 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12441     if ( CheckIfFontClosed(self) ) return (-1);				\
12442 return( PyFF_Font_set_int(self,value,#name,offsetof(SplineFont,name)) );\
12443 }
12444 
12445 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12446 #define ff_gs_cidint(name) \
12447 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12448     if ( CheckIfFontClosed(self) ) return (NULL);		\
12449     if ( self->fv->cidmaster==NULL )				\
12450 Py_RETURN_NONE;							\
12451 return( Py_BuildValue("i", self->fv->cidmaster->name ));	\
12452 }								\
12453 								\
12454 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12455     if ( CheckIfFontClosed(self) ) return (-1);				   \
12456 return( PyFF_Font_set_cidint(self,value,#name,offsetof(SplineFont,name)) );\
12457 }
12458 
12459 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12460 #define ff_gs_int2(name) \
12461 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12462     if ( CheckIfFontClosed(self) ) return (NULL);	\
12463 return( Py_BuildValue("i", self->fv->sf->name ));	\
12464 }							\
12465 							\
12466 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12467     if ( CheckIfFontClosed(self) ) return (-1);				 \
12468 return( PyFF_Font_set_int2(self,value,#name,offsetof(SplineFont,name)) );\
12469 }
12470 
12471 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12472 #define ff_gs_ro_bit(name) \
12473 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12474     if ( CheckIfFontClosed(self) ) return (NULL);	\
12475 return( Py_BuildValue("i", self->fv->sf->name ));	\
12476 }
12477 
12478 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12479 #define ff_gs_bit(name) \
12480 static PyObject *PyFF_Font_get_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12481     if ( CheckIfFontClosed(self) ) return (NULL);	\
12482 return( Py_BuildValue("i", self->fv->sf->name ));	\
12483 }							\
12484 							\
12485 static int PyFF_Font_set_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) { \
12486     long temp;						\
12487 							\
12488     if ( CheckIfFontClosed(self) ) return (-1);		\
12489     if ( value==NULL ) {				\
12490 	PyErr_SetString(PyExc_TypeError, "Cannot delete the " #name ); \
12491 return( -1 );						\
12492     }							\
12493     temp = PyLong_AsLong(value);				\
12494     if ( PyErr_Occurred()!=NULL )			\
12495 return( -1 );						\
12496     self->fv->sf->name = temp;				\
12497 return( 0 );						\
12498 }
12499 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
SFDefaultOS2(SplineFont * sf)12500 static void SFDefaultOS2(SplineFont *sf) {
12501     if ( !sf->pfminfo.pfmset ) {
12502 	SFDefaultOS2Info(&sf->pfminfo,sf,sf->fontname);
12503 	sf->pfminfo.pfmset = sf->pfminfo.subsuper_set = sf->pfminfo.panose_set =
12504 	    sf->pfminfo.hheadset = sf->pfminfo.vheadset = true;
12505     }
12506 }
12507 
12508 #define ff_gs_os2int2(name) \
12509 static PyObject *PyFF_Font_get_OS2_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12510     SplineFont *sf;					\
12511     if ( CheckIfFontClosed(self) ) return (NULL);	\
12512     sf = self->fv->sf;					\
12513     SFDefaultOS2(sf);					\
12514 return( Py_BuildValue("i", sf->pfminfo.name ));		\
12515 }							\
12516 							\
12517 static int PyFF_Font_set_OS2_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {\
12518     SplineFont *sf;					\
12519     if ( CheckIfFontClosed(self) ) return (-1);		\
12520     sf = self->fv->sf;					\
12521     SFDefaultOS2(sf);					\
12522 return( PyFF_Font_set_int2(self,value,#name,offsetof(SplineFont,pfminfo)+offsetof(struct pfminfo,name)) );\
12523 }
12524 /* *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  */
12525 #define ff_gs_os2bit(name) \
12526 static PyObject *PyFF_Font_get_OS2_##name(PyFF_Font *self, void *UNUSED(closure)) { \
12527     SplineFont *sf;					\
12528     if ( CheckIfFontClosed(self) ) return (NULL);	\
12529     sf = self->fv->sf;					\
12530     SFDefaultOS2(sf);					\
12531 return( Py_BuildValue("i", self->fv->sf->pfminfo.name ));	\
12532 }							\
12533 							\
12534 static int PyFF_Font_set_OS2_##name(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) { \
12535     SplineFont *sf;					\
12536     long temp;						\
12537 							\
12538     if ( CheckIfFontClosed(self) ) return (-1);		\
12539     sf = self->fv->sf;					\
12540     if ( value==NULL ) {				\
12541 	PyErr_SetString(PyExc_TypeError, "Cannot delete the " #name ); \
12542 return( -1 );						\
12543     }							\
12544     temp = PyLong_AsLong(value);				\
12545     if ( PyErr_Occurred()!=NULL )			\
12546 return( -1 );						\
12547     SFDefaultOS2(sf);					\
12548     sf->pfminfo.name = temp;				\
12549 return( 0 );						\
12550 }
12551 
12552 ff_gs_str(fontname)
ff_gs_str(fullname)12553 ff_gs_str(fullname)
12554 ff_gs_str(familyname)
12555 ff_gs_str(weight)
12556 ff_gs_str(version)
12557 
12558 ff_gs_strnull(comments)
12559 ff_gs_strnull(fontlog)
12560 ff_gs_strnull(copyright)
12561 ff_gs_strnull(xuid)
12562 ff_gs_strnull(fondname)
12563 ff_gs_strnull(woffMetadata)
12564 ff_gs_strnull(defbasefilename)
12565 
12566 ff_gs_cidstrnull(fontname)
12567 ff_gs_cidstrnull(familyname)
12568 ff_gs_cidstrnull(fullname)
12569 ff_gs_cidstrnull(weight)
12570 ff_gs_cidstrnull(copyright)
12571 ff_gs_cidstrnull(cidregistry)
12572 ff_gs_cidstrnull(ordering)
12573 
12574 ff_gs_cidreal(cidversion)
12575 ff_gs_cidint(supplement)
12576 
12577 ff_gs_real(italicangle)
12578 ff_gs_real(upos)
12579 ff_gs_real(uwidth)
12580 ff_gs_real(strokewidth)
12581 
12582 ff_gs_int(ascent)
12583 ff_gs_int(descent)
12584 ff_gs_int(uniqueid)
12585 
12586 ff_gs_int2(macstyle)
12587 ff_gs_int2(os2_version)
12588 ff_gs_int2(gasp_version)
12589 
12590 ff_gs_os2int2(weight)
12591 ff_gs_os2int2(width)
12592 ff_gs_os2int2(stylemap)
12593 ff_gs_os2int2(fstype)
12594 ff_gs_os2int2(linegap)
12595 ff_gs_os2int2(vlinegap)
12596 ff_gs_os2int2(hhead_ascent)
12597 ff_gs_os2int2(hhead_descent)
12598 ff_gs_os2int2(os2_typoascent)
12599 ff_gs_os2int2(os2_typodescent)
12600 ff_gs_os2int2(os2_typolinegap)
12601 ff_gs_os2int2(os2_winascent)
12602 ff_gs_os2int2(os2_windescent)
12603 ff_gs_os2int2(os2_subxsize)
12604 ff_gs_os2int2(os2_subxoff)
12605 ff_gs_os2int2(os2_subysize)
12606 ff_gs_os2int2(os2_subyoff)
12607 ff_gs_os2int2(os2_supxsize)
12608 ff_gs_os2int2(os2_supxoff)
12609 ff_gs_os2int2(os2_supysize)
12610 ff_gs_os2int2(os2_supyoff)
12611 ff_gs_os2int2(os2_strikeysize)
12612 ff_gs_os2int2(os2_strikeypos)
12613 ff_gs_os2int2(os2_capheight)
12614 ff_gs_os2int2(os2_xheight)
12615 ff_gs_os2int2(os2_family_class)
12616 
12617 ff_gs_os2bit(winascent_add)
12618 ff_gs_os2bit(windescent_add)
12619 ff_gs_os2bit(hheadascent_add)
12620 ff_gs_os2bit(hheaddescent_add)
12621 ff_gs_os2bit(typoascent_add)
12622 ff_gs_os2bit(typodescent_add)
12623 
12624 ff_gs_bit(changed)
12625 ff_gs_ro_bit(multilayer)
12626 ff_gs_bit(strokedfont)
12627 ff_gs_ro_bit(new)
12628 
12629 ff_gs_bit(use_typo_metrics)
12630 ff_gs_bit(weight_width_slope_only)
12631 ff_gs_bit(onlybitmaps)
12632 ff_gs_bit(hasvmetrics)
12633 ff_gs_bit(head_optimized_for_cleartype)
12634 
12635 static PyObject *PyFF_Font_get_creationtime(PyFF_Font *self, void *UNUSED(closure)) {
12636     if ( CheckIfFontClosed(self) )
12637 return(NULL);
12638 
12639   SplineFont *sf = self->fv->sf;
12640   time_t t = sf->creationtime;
12641   const struct tm *tm = gmtime(&t);
12642   char creationtime[200];
12643   strftime(creationtime, sizeof(creationtime), "%Y/%m/%d %H:%M:%S", tm);
12644 return Py_BuildValue("s", creationtime);
12645 }
12646 
PyFF_Font_get_sfntRevision(PyFF_Font * self,void * UNUSED (closure))12647 static PyObject *PyFF_Font_get_sfntRevision(PyFF_Font *self, void *UNUSED(closure)) {
12648     int version = self->fv->sf->sfntRevision;
12649 
12650     if ( CheckIfFontClosed(self) )
12651 return(NULL);
12652     if ( version==sfntRevisionUnset )
12653 Py_RETURN_NONE;
12654 
12655 return( Py_BuildValue("d", version/65536.0 ));
12656 }
12657 
PyFF_Font_set_sfntRevision(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12658 static int PyFF_Font_set_sfntRevision(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12659     SplineFont *sf;
12660 
12661     if ( CheckIfFontClosed(self) )
12662 return(-1);
12663     sf = self->fv->sf;
12664     if ( value==Py_None )
12665 	sf->sfntRevision = sfntRevisionUnset;
12666     else if ( PyFloat_Check(value)) {
12667 	double temp = PyFloat_AsDouble(value);
12668 
12669 	sf->sfntRevision = rint(65536*temp);
12670     } else if ( PyLong_Check(value)) {
12671 	int val = PyLong_AsLong(value);
12672 	/* if ( val<100 )
12673 	    sf->sfntRevision = val<<16;
12674 	else*/
12675 	    sf->sfntRevision = val;
12676     } else {
12677 	PyErr_Format(PyExc_TypeError, "Value must be a double, integer or None" );
12678 return( -1 );
12679     }
12680 
12681 return( 0 );
12682 }
12683 
PyFF_Font_get_woffMajor(PyFF_Font * self,void * UNUSED (closure))12684 static PyObject *PyFF_Font_get_woffMajor(PyFF_Font *self, void *UNUSED(closure)) {
12685     int version;
12686 
12687     if ( CheckIfFontClosed(self) )
12688 return (NULL);
12689     version = self->fv->sf->woffMajor;
12690     if ( version==woffUnset )
12691 Py_RETURN_NONE;
12692 
12693 return( Py_BuildValue("i", version ));
12694 }
12695 
PyFF_Font_set_woffMajor(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12696 static int PyFF_Font_set_woffMajor(PyFF_Font *self, PyObject *value, void *UNUSED(closure)) {
12697     SplineFont *sf;
12698 
12699     if ( CheckIfFontClosed(self) )
12700 return (-1);
12701     sf = self->fv->sf;
12702     if ( value==Py_None ) {
12703 	sf->woffMajor = woffUnset;
12704 	sf->woffMinor = woffUnset;
12705     } else if ( PyLong_Check(value)) {
12706 	int val = PyLong_AsLong(value);
12707 	sf->woffMajor = val;
12708 	if ( sf->woffMinor==woffUnset )
12709 	    sf->woffMinor = 0;
12710     } else {
12711 	PyErr_Format(PyExc_TypeError, "Value must be an integer or None" );
12712 return( -1 );
12713     }
12714 
12715 return( 0 );
12716 }
12717 
PyFF_Font_get_woffMinor(PyFF_Font * self,void * UNUSED (closure))12718 static PyObject *PyFF_Font_get_woffMinor(PyFF_Font *self, void *UNUSED(closure)) {
12719     SplineFont *sf;
12720 
12721     if ( CheckIfFontClosed(self) )
12722 return (NULL);
12723     sf = self->fv->sf;
12724     if ( sf->woffMajor==woffUnset )
12725 Py_RETURN_NONE;
12726 
12727 return( Py_BuildValue("i", sf->woffMinor ));
12728 }
12729 
PyFF_Font_set_woffMinor(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12730 static int PyFF_Font_set_woffMinor(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12731     SplineFont *sf;
12732 
12733     if ( CheckIfFontClosed(self) )
12734 return (-1);
12735     sf = self->fv->sf;
12736     if ( value==Py_None ) {
12737 	sf->woffMajor = woffUnset;
12738 	sf->woffMinor = woffUnset;
12739     } else if ( PyLong_Check(value)) {
12740 	int val = PyLong_AsLong(value);
12741 	sf->woffMinor = val;
12742 	if ( sf->woffMajor==woffUnset )
12743 	    sf->woffMajor = 0;
12744     } else {
12745 	PyErr_Format(PyExc_TypeError, "Value must be an integer or None" );
12746 return( -1 );
12747     }
12748 
12749 return( 0 );
12750 }
12751 
PyFF_Font_get_vertical_origin(PyFF_Font * self,void * UNUSED (closure))12752 static PyObject *PyFF_Font_get_vertical_origin(PyFF_Font *self, void *UNUSED(closure)) {
12753     if ( CheckIfFontClosed(self) )
12754 return (NULL);
12755 return( Py_BuildValue("i", 0 )); /* No longer implemented, return 0 for backwards compatability */
12756 }
12757 
PyFF_Font_set_vertical_origin(PyFF_Font * UNUSED (self),PyObject * UNUSED (value),void * UNUSED (closure))12758 static int PyFF_Font_set_vertical_origin(PyFF_Font *UNUSED(self), PyObject *UNUSED(value), void *UNUSED(closure)) {
12759     PyErr_Format(PyExc_NotImplementedError, "No longer supported");
12760 return( -1 );
12761 }
12762 
PyFF_Font_get_os2codepages(PyFF_Font * self,void * UNUSED (closure))12763 static PyObject *PyFF_Font_get_os2codepages(PyFF_Font *self, void *UNUSED(closure)) {
12764     SplineFont *sf;
12765 
12766     if ( CheckIfFontClosed(self) )
12767 return (NULL);
12768     sf = self->fv->sf;
12769     if ( !sf->pfminfo.hascodepages )
12770 	OS2FigureCodePages(sf,sf->pfminfo.codepages);
12771 	/* Don't mark it as having them though */
12772 return( Py_BuildValue("(ii)", sf->pfminfo.codepages[0],sf->pfminfo.codepages[1]));
12773 }
12774 
PyFF_Font_set_os2codepages(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12775 static int PyFF_Font_set_os2codepages(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12776     SplineFont *sf;
12777 
12778     if ( CheckIfFontClosed(self) )
12779 return (-1);
12780     sf = self->fv->sf;
12781     if ( value == NULL ) {
12782         sf->pfminfo.hascodepages = false;
12783 return( 0 );
12784     }
12785 
12786     if ( !PyArg_ParseTuple(value,"ii", &sf->pfminfo.codepages[0], &sf->pfminfo.codepages[1]))
12787 return(-1);
12788     sf->pfminfo.hascodepages = true;
12789 return( 0 );
12790 }
12791 
PyFF_Font_get_os2unicoderanges(PyFF_Font * self,void * UNUSED (closure))12792 static PyObject *PyFF_Font_get_os2unicoderanges(PyFF_Font *self, void *UNUSED(closure)) {
12793     SplineFont *sf;
12794 
12795     if ( CheckIfFontClosed(self) )
12796 return (NULL);
12797     sf = self->fv->sf;
12798     if ( !sf->pfminfo.hasunicoderanges )
12799 	OS2FigureUnicodeRanges(sf,sf->pfminfo.unicoderanges);
12800 	/* Don't mark it as having them though */
12801 return( Py_BuildValue("(iiii)",
12802 	sf->pfminfo.unicoderanges[0],sf->pfminfo.unicoderanges[1],
12803 	sf->pfminfo.unicoderanges[2], sf->pfminfo.unicoderanges[3]));
12804 }
12805 
PyFF_Font_set_os2unicoderanges(PyFF_Font * self,PyObject * value,void * UNUSED (closure))12806 static int PyFF_Font_set_os2unicoderanges(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
12807     SplineFont *sf;
12808 
12809     if ( CheckIfFontClosed(self) )
12810 return (-1);
12811     sf = self->fv->sf;
12812     if ( value == NULL ) {
12813         sf->pfminfo.hasunicoderanges = false;
12814 return( 0 );
12815     }
12816 
12817     if ( !PyArg_ParseTuple(value,"iiii",
12818 	    &sf->pfminfo.unicoderanges[0], &sf->pfminfo.unicoderanges[1],
12819 	    &sf->pfminfo.unicoderanges[2], &sf->pfminfo.unicoderanges[3]))
12820 return(-1);
12821     sf->pfminfo.hasunicoderanges = true;
12822 return( 0 );
12823 }
12824 
PyFF_Font_get_loadvalidation_state(PyFF_Font * self,void * UNUSED (closure))12825 static PyObject *PyFF_Font_get_loadvalidation_state(PyFF_Font *self, void *UNUSED(closure)) {
12826     if ( CheckIfFontClosed(self) )
12827 return (NULL);
12828 return( Py_BuildValue("i", self->fv->sf->loadvalidation_state));
12829 }
12830 
PyFF_Font_get_privatevalidation_state(PyFF_Font * self,void * UNUSED (closure))12831 static PyObject *PyFF_Font_get_privatevalidation_state(PyFF_Font *self, void *UNUSED(closure)) {
12832     if ( CheckIfFontClosed(self) )
12833 return (NULL);
12834 return( Py_BuildValue("i", ValidatePrivate(self->fv->sf)));
12835 }
12836 
PyFF_Font_get_baseline(PyFF_Font * self,void * UNUSED (closure),struct Base * base)12837 static PyObject *PyFF_Font_get_baseline(PyFF_Font *self, void *UNUSED(closure), struct Base *base) {
12838     PyObject *ret, *scripts, *langs, *features, *tags, *script, *poses, *lang, *feature;
12839     int cnt,i,j,k;
12840     struct basescript *bs;
12841     struct baselangextent *bl, *feat;
12842 
12843     if ( CheckIfFontClosed(self) )
12844 return (NULL);
12845     if ( base==NULL )
12846 Py_RETURN_NONE;
12847 
12848     ret  = PyTuple_New(2);
12849     tags = PyTuple_New(base->baseline_cnt);
12850     PyTuple_SetItem(ret,0,tags);
12851     for ( i=0; i<base->baseline_cnt; ++i )
12852 	PyTuple_SetItem(tags,i,TagToPythonString(base->baseline_tags[i],false));
12853 
12854     for ( bs=base->scripts, cnt=0; bs!=NULL; bs=bs->next, ++cnt );
12855     scripts = PyTuple_New(cnt);
12856     PyTuple_SetItem(ret,1,scripts);
12857     for ( bs=base->scripts, i=0; bs!=NULL; bs=bs->next, ++i ) {
12858 	script = PyTuple_New(4);
12859 	PyTuple_SetItem(scripts,i,script);
12860 	PyTuple_SetItem(script,0,TagToPythonString(bs->script,false));
12861 	if ( base->baseline_cnt==0 ) {
12862 	    Py_INCREF(Py_None); Py_INCREF(Py_None);
12863 	    PyTuple_SetItem(script,1,Py_None);
12864 	    PyTuple_SetItem(script,2,Py_None);
12865 	} else {
12866 	    PyTuple_SetItem(script,1,TagToPythonString(base->baseline_tags[bs->def_baseline],false));
12867 	    poses = PyTuple_New(base->baseline_cnt);
12868 	    for ( j=0; j<base->baseline_cnt; ++j )
12869 		PyTuple_SetItem(poses,j,PyLong_FromLong(bs->baseline_pos[j]));
12870 	    PyTuple_SetItem(script,2,poses);
12871 	}
12872 	for ( j=0, bl=bs->langs; bl!=NULL; bl=bl->next, ++j );
12873 	langs = PyTuple_New(j);
12874 	PyTuple_SetItem(script,3,langs);
12875 	for ( j=0, bl=bs->langs; bl!=NULL; bl=bl->next, ++j ) {
12876 	    lang = PyTuple_New(4);
12877 	    PyTuple_SetItem(langs,j,lang);
12878 	    PyTuple_SetItem(lang,0,TagToPythonString(bl->lang,false));
12879 	    PyTuple_SetItem(lang,1,PyLong_FromLong(bl->descent));
12880 	    PyTuple_SetItem(lang,2,PyLong_FromLong(bl->ascent));
12881 	    for ( k=0, feat=bl->features; feat!=NULL; feat=feat->next, ++k );
12882 	    features = PyTuple_New(k);
12883 	    PyTuple_SetItem(lang,3,features);
12884 	    for ( k=0, feat=bl->features; feat!=NULL; feat=feat->next, ++k ) {
12885 		feature = PyTuple_New(3);
12886 		PyTuple_SetItem(features,k,feature);
12887 		PyTuple_SetItem(feature,0,TagToPythonString(feat->lang,false));
12888 		PyTuple_SetItem(feature,1,PyLong_FromLong(feat->descent));
12889 		PyTuple_SetItem(feature,2,PyLong_FromLong(feat->ascent));
12890 	    }
12891 	}
12892     }
12893 return( ret );
12894 }
12895 
PyFF_Font_get_horizontal_baseline(PyFF_Font * self,void * closure)12896 static PyObject *PyFF_Font_get_horizontal_baseline(PyFF_Font *self, void *closure) {
12897     if ( CheckIfFontClosed(self) )
12898 return (NULL);
12899 return( PyFF_Font_get_baseline(self,closure,self->fv->sf->horiz_base));
12900 }
12901 
PyFF_Font_get_vertical_baseline(PyFF_Font * self,void * closure)12902 static PyObject *PyFF_Font_get_vertical_baseline(PyFF_Font *self, void *closure) {
12903     if ( CheckIfFontClosed(self) )
12904 return (NULL);
12905 return( PyFF_Font_get_baseline(self,closure,self->fv->sf->vert_base));
12906 }
12907 
PyFF_Font_set_baseline(PyFF_Font * self,PyObject * value,void * UNUSED (closure),struct Base ** basep)12908 static int PyFF_Font_set_baseline(PyFF_Font *self, PyObject *value, void *UNUSED(closure),struct Base **basep) {
12909     PyObject *basetags, *scripts;
12910     int basecnt,i;
12911     struct Base *base;
12912     int scriptcnt,langcnt,featcnt,j,k;
12913     struct basescript *bs = NULL, *lastbs;
12914     struct baselangextent *ln, *lastln;
12915     struct baselangextent *ft, *lastft;
12916 
12917     if ( CheckIfFontClosed(self) )
12918 return (-1);
12919     if ( value==Py_None ) {
12920 	BaseFree(*basep);
12921 	*basep = NULL;
12922 return( 0 );
12923     }
12924     if ( !PyArg_ParseTuple(value,"OO", &basetags, &scripts))
12925 return( -1 );
12926     if ( basetags==Py_None )
12927 	basecnt = 0;
12928     else {
12929 	basecnt = PyTuple_Size(basetags);
12930 	if ( basecnt<0 )
12931 return( -1 );
12932     }
12933     base = chunkalloc(sizeof( struct Base));
12934     base->baseline_cnt = basecnt;
12935     base->baseline_tags = malloc(basecnt*sizeof(uint32));
12936     base->scripts = NULL;
12937     for ( i=0; i<basecnt; ++i ) {
12938 	PyObject *str = PyTuple_GetItem(basetags,i);
12939 	if ( !PyUnicode_Check(str) ) {
12940 	    PyErr_Format(PyExc_TypeError, "Baseline tag must be a 4 character string" );
12941 	    BaseFree(base);
12942 return( -1 );
12943 	}
12944 	base->baseline_tags[i] = StrObjToTag(str,NULL);
12945 	if ( base->baseline_tags[i]==BAD_TAG ) {
12946 	    BaseFree(base);
12947 return( -1 );
12948 	}
12949     }
12950 
12951     lastbs = NULL;
12952     scriptcnt = PyTuple_Size(scripts);
12953     if ( scriptcnt<0 ) {
12954 	BaseFree(base);
12955 return( -1 );
12956     }
12957     for ( j=0; j<scriptcnt; ++j ) {
12958 	PyObject *script = PyTuple_GetItem(scripts,j);
12959 	char *scripttag, *def_baseln;
12960 	PyObject *offsets, *langs;
12961 
12962 	if ( !PyArg_ParseTuple(script,"szOO",&scripttag,&def_baseln,&offsets,&langs)) {
12963 	    BaseFree(base);
12964 return( -1 );
12965 	}
12966 	bs = chunkalloc(sizeof(struct basescript));
12967 	bs->next = NULL;
12968 	if ( lastbs==NULL )
12969 	    base->scripts = bs;
12970 	else
12971 	    lastbs->next = bs;
12972 	lastbs = bs;
12973 	bs->script = StrToTag(scripttag,NULL);
12974 	if ( bs->script == BAD_TAG ) {
12975 	    BaseFree(base);
12976 return( -1 );
12977 	}
12978 	if ( basecnt==0 && (def_baseln==NULL && offsets==NULL))
12979 	    /* That's reasonable */;
12980 	else if ( basecnt!=0 && (def_baseln!=NULL && offsets!=NULL)) {
12981 	    /* Also reasonable */;
12982 	    uint32 tag = StrToTag(def_baseln,NULL);
12983 	    if ( tag==BAD_TAG ) {
12984 		BaseFree(base);
12985 return( -1 );
12986 	    }
12987 	    if ( PyTuple_Size(offsets)!=basecnt ) {
12988 		PyErr_Format(PyExc_TypeError, "There must be as many baseline positions as there are baslines, in a script" );
12989 		BaseFree(base);
12990 return( -1 );
12991 	    }
12992 	    for ( i=0; i<basecnt && base->baseline_tags[i]!=tag; ++i );
12993 	    if ( i==basecnt ) {
12994 		PyErr_Format(PyExc_TypeError, "A script's default baseline must be one of the baselines specified" );
12995 		BaseFree(base);
12996 return( -1 );
12997 	    }
12998 	    bs->def_baseline = i;
12999 	    bs->baseline_pos = malloc(basecnt*sizeof(int16));
13000 	    for ( i=0; i<basecnt; ++i ) {
13001 		if ( !PyLong_Check(PyTuple_GetItem(offsets,i))) {
13002 		    PyErr_Format(PyExc_TypeError, "Baseline positions must be integers");
13003 		    BaseFree(base);
13004 return( -1 );
13005 		}
13006 		bs->baseline_pos[i] = PyLong_AsLong(PyTuple_GetItem(offsets,i));
13007 	    }
13008 	} else {
13009 	    BaseFree(base);
13010 	    if ( basecnt==0 )
13011 		PyErr_Format(PyExc_TypeError, "You did not specify any baselines, so you may not specify a default baseline, nor offsets in a script" );
13012 	    else
13013 		PyErr_Format(PyExc_TypeError, "You must specify a default baseline and offsets in a script" );
13014 return( -1 );
13015 	}
13016 
13017 	if ( langs==Py_None )
13018     continue;			/* That's ok */
13019 	if ( !PyTuple_Check(langs)) {
13020 	    PyErr_Format(PyExc_TypeError, "The languages must be specified by a tuple");
13021 	    BaseFree(base);
13022 return( -1 );
13023 	}
13024 	lastln = NULL;
13025 	langcnt = PyTuple_Size(langs);
13026 	for ( i=0; i<langcnt; ++i ) {
13027 	    PyObject *lang = PyTuple_GetItem(langs,i);
13028 	    char *tag;
13029 	    int min,max;
13030 	    PyObject *features;
13031 
13032 	    if ( !PyArg_ParseTuple(lang,"siiO",&tag,&min,&max,&features)) {
13033 		BaseFree(base);
13034 return( -1 );
13035 	    }
13036 	    ln = chunkalloc(sizeof(struct baselangextent));
13037 	    if ( lastln==NULL )
13038 		bs->langs = ln;
13039 	    else
13040 		lastln->next = ln;
13041 	    lastln = ln;
13042 	    ln->lang = StrToTag(tag,NULL);
13043 	    if ( ln->lang == BAD_TAG ) {
13044 		PyErr_Format(PyExc_TypeError, "A language tag must be a 4 character string");
13045 		BaseFree(base);
13046 return( -1 );
13047 	    }
13048 	    ln->descent = min;
13049 	    ln->ascent  = max;
13050 
13051 	    if ( features==Py_None )
13052 	continue;			/* That's ok */
13053 	    if ( !PyTuple_Check(features)) {
13054 		PyErr_Format(PyExc_TypeError, "The features must be specified by a tuple");
13055 		BaseFree(base);
13056 return( -1 );
13057 	    }
13058 	    lastft = NULL;
13059 	    featcnt = PyTuple_Size(features);
13060 	    for ( k=0; k<featcnt; ++k ) {
13061 		PyObject *feat = PyTuple_GetItem(features,k);
13062 		char *tag;
13063 		int min,max;
13064 
13065 		if ( !PyArg_ParseTuple(feat,"sii",&tag,&min,&max)) {
13066 		    BaseFree(base);
13067 return( -1 );
13068 		}
13069 		ft = chunkalloc(sizeof(struct baselangextent));
13070 		if ( lastft==NULL )
13071 		    ln->features = ft;
13072 		else
13073 		    lastft->next = ft;
13074 		lastft = ft;
13075 		ft->lang = StrToTag(tag,NULL);
13076 		if ( ft->lang == BAD_TAG ) {
13077 		    BaseFree(base);
13078 return( -1 );
13079 		}
13080 		ft->descent = min;
13081 		ft->ascent  = max;
13082 	    }
13083 	}
13084     }
13085     BaseFree(*basep);
13086     *basep = base;
13087 return( 0 );
13088 }
13089 
PyFF_Font_set_horizontal_baseline(PyFF_Font * self,PyObject * value,void * closure)13090 static int PyFF_Font_set_horizontal_baseline(PyFF_Font *self,PyObject *value, void *closure) {
13091     if ( CheckIfFontClosed(self) )
13092 return (-1);
13093 return( PyFF_Font_set_baseline(self,value,closure,&self->fv->sf->horiz_base));
13094 }
13095 
PyFF_Font_set_vertical_baseline(PyFF_Font * self,PyObject * value,void * closure)13096 static int PyFF_Font_set_vertical_baseline(PyFF_Font *self,PyObject *value, void *closure) {
13097     if ( CheckIfFontClosed(self) )
13098 return (-1);
13099 return( PyFF_Font_set_baseline(self,value,closure,&self->fv->sf->vert_base));
13100 }
13101 
PyFF_Font_get_path(PyFF_Font * self,void * UNUSED (closure))13102 static PyObject *PyFF_Font_get_path(PyFF_Font *self, void *UNUSED(closure)) {
13103     if ( CheckIfFontClosed(self) )
13104 return (NULL);
13105     if ( self->fv->sf->origname==NULL )
13106 Py_RETURN_NONE;
13107     else
13108 return( Py_BuildValue("s", self->fv->sf->origname ));
13109 }
13110 
PyFF_Font_get_sfd_path(PyFF_Font * self,void * UNUSED (closure))13111 static PyObject *PyFF_Font_get_sfd_path(PyFF_Font *self, void *UNUSED(closure)) {
13112     if ( CheckIfFontClosed(self) )
13113 return (NULL);
13114     if ( self->fv->sf->filename==NULL )
13115 Py_RETURN_NONE;
13116     else
13117 return( Py_BuildValue("s", self->fv->sf->filename ));
13118 }
13119 
PyFF_Font_get_OS2_panose(PyFF_Font * self,void * UNUSED (closure))13120 static PyObject *PyFF_Font_get_OS2_panose(PyFF_Font *self, void *UNUSED(closure)) {
13121     int i;
13122     PyObject *tuple;
13123     SplineFont *sf;
13124 
13125     if ( CheckIfFontClosed(self) )
13126 return (NULL);
13127     sf = self->fv->sf;
13128     if ( !sf->pfminfo.panose_set )
13129 	SFDefaultOS2(sf);
13130     tuple = PyTuple_New(10);
13131     for ( i=0; i<10; ++i )
13132 	PyTuple_SET_ITEM(tuple,i,Py_BuildValue("i",self->fv->sf->pfminfo.panose[i]));
13133 return( tuple );
13134 }
13135 
PyFF_Font_set_OS2_panose(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13136 static int PyFF_Font_set_OS2_panose(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13137     int panose[10], i;
13138     SplineFont *sf;
13139 
13140     if ( CheckIfFontClosed(self) )
13141 return (-1);
13142     sf = self->fv->sf;
13143     if ( value==NULL ) {
13144 	PyErr_Format(PyExc_TypeError, "Cannot delete panose");
13145 return( -1 );
13146     }
13147     if ( !PyArg_ParseTuple(value,"iiiiiiiiii", &panose[0], &panose[1], &panose[2], &panose[3],
13148 	    &panose[4], &panose[5], &panose[6], &panose[7], &panose[8], &panose[9] ))
13149 return( -1 );
13150 
13151     if ( !sf->pfminfo.panose_set )
13152 	SFDefaultOS2(sf);
13153     for ( i=0; i<10; ++i )
13154 	sf->pfminfo.panose[i] = panose[i];
13155     sf->pfminfo.panose_set = true;
13156 return( 0 );
13157 }
13158 
PyFF_Font_get_OS2_vendor(PyFF_Font * self,void * UNUSED (closure))13159 static PyObject *PyFF_Font_get_OS2_vendor(PyFF_Font *self, void *UNUSED(closure)) {
13160     char buf[8];
13161     SplineFont *sf;
13162 
13163     if ( CheckIfFontClosed(self) )
13164 return (NULL);
13165     sf = self->fv->sf;
13166     SFDefaultOS2(sf);
13167     buf[0] = sf->pfminfo.os2_vendor[0];
13168     buf[1] = sf->pfminfo.os2_vendor[1];
13169     buf[2] = sf->pfminfo.os2_vendor[2];
13170     buf[3] = sf->pfminfo.os2_vendor[3];
13171     buf[4] = '\0';
13172 return( Py_BuildValue("s",buf));
13173 }
13174 
PyFF_Font_set_OS2_vendor(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13175 static int PyFF_Font_set_OS2_vendor(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13176     SplineFont *sf;
13177     const char *newv;
13178 
13179     if ( CheckIfFontClosed(self) )
13180 return (-1);
13181     sf = self->fv->sf;
13182     if ( value==NULL ) {
13183 	PyErr_Format(PyExc_TypeError, "Cannot delete vendor" );
13184 return( -1 );
13185     } else if ((newv = PyUnicode_AsUTF8(value)) == NULL) {
13186         return -1;
13187     }
13188 
13189     if ( strlen( newv )>4 ) {
13190 	PyErr_Format(PyExc_TypeError, "OS2 vendor is limited to 4 characters" );
13191 return( -1 );
13192     }
13193     SFDefaultOS2(sf);
13194     strncpy(sf->pfminfo.os2_vendor, newv, 4);
13195     sf->pfminfo.panose_set = true;
13196 return( 0 );
13197 }
13198 
PyFF_Font_get_design_size(PyFF_Font * self,void * UNUSED (closure))13199 static PyObject *PyFF_Font_get_design_size(PyFF_Font *self, void *UNUSED(closure)) {
13200     /* Design size is expressed in tenths of points */
13201     if ( CheckIfFontClosed(self) )
13202 return (NULL);
13203 return( Py_BuildValue("d", self->fv->sf->design_size/10.0));
13204 }
13205 
PyFF_Font_set_design_size(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13206 static int PyFF_Font_set_design_size(PyFF_Font *self, PyObject *value,
13207 				     void *UNUSED(closure)) {
13208     double temp;
13209 
13210     if ( CheckIfFontClosed(self) )
13211 return (-1);
13212     if ( value==NULL )
13213 	self->fv->sf->design_size = 0;
13214     else if ( PyFloat_Check(value)) {
13215 	temp = PyFloat_AsDouble(value);
13216 	if ( PyErr_Occurred()!=NULL )
13217 return( -1 );
13218 	self->fv->sf->design_size = rint(10.0*temp);
13219     } else if ( PyLong_Check(value)) {
13220 	int t = PyLong_AsLong(value);
13221 	if ( PyErr_Occurred()!=NULL )
13222 return( -1 );
13223 	self->fv->sf->design_size = 10*t;
13224     }
13225 return( 0 );
13226 }
13227 
PyFF_Font_get_size_feature(PyFF_Font * self,void * UNUSED (closure))13228 static PyObject *PyFF_Font_get_size_feature(PyFF_Font *self, void *UNUSED(closure)) {
13229     /* Size feature has two formats: Just a design size, or a design size */
13230     /*  and size bounds, id & name */
13231     /* First case means a tuple of one element, second a tuple of 5 */
13232     /* design size & bounds are measured in tenths of points */
13233     struct otfname *names;
13234     int i,cnt;
13235     PyObject *tuple;
13236     SplineFont *sf;
13237 
13238     if ( CheckIfFontClosed(self) )
13239         return (NULL);
13240     sf = self->fv->sf;
13241     if ( sf->design_size==0 )
13242         Py_RETURN_NONE;
13243 
13244     for ( names=sf->fontstyle_name, cnt=0; names!=NULL; names=names->next, ++cnt );
13245     if ( cnt==0 )
13246         return( Py_BuildValue("(d)", sf->design_size/10.0));
13247     tuple = PyTuple_New(cnt);
13248     for ( names=sf->fontstyle_name, cnt=0; names!=NULL; names=names->next, ++cnt ) {
13249 	for ( i=0; sfnt_name_mslangs[i].name!=NULL ; ++i ) {
13250 	    if ( sfnt_name_mslangs[i].flag == names->lang )
13251                 break;
13252         }
13253 	if ( sfnt_name_mslangs[i].flag == names->lang ) {
13254             PyTuple_SetItem(tuple,cnt,Py_BuildValue("ss", sfnt_name_mslangs[i].name, names->name));
13255         } else {
13256             PyTuple_SetItem(tuple,cnt,Py_BuildValue("is", names->lang, names->name));
13257         }
13258     }
13259     return( Py_BuildValue("(dddiO)", sf->design_size/10.0,
13260 	sf->design_range_bottom/10.0, sf->design_range_top/10.,
13261 	sf->fontstyle_id, tuple ));
13262 }
13263 
PyFF_Font_set_size_feature(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13264 static int PyFF_Font_set_size_feature(PyFF_Font *self,PyObject *value,
13265 				      void *UNUSED(closure)) {
13266     double temp, top=0, bot=0;
13267     int id=0;
13268     PyObject *names=NULL;
13269     SplineFont *sf;
13270     int i;
13271     struct otfname *head, *last, *cur;
13272     const char *string;
13273 
13274     if ( CheckIfFontClosed(self) )
13275 return (-1);
13276     sf = self->fv->sf;
13277     if ( value==NULL || value==Py_None ) {
13278 	sf->design_size = 0;
13279 	/* Setting the design size to zero means there will be no 'size' feature in the output */
13280 return( 0 );
13281     }
13282     /* If they only specify a design size, we won't require that it be a tuple */
13283     /*  if it is in a tuple, remove it and treat as a single value */
13284     if ( PyTuple_Size(value)==1 )
13285 	value = PyTuple_GetItem(value,0);
13286     if ( PyFloat_Check(value) || PyLong_Check(value)) {
13287 	if ( PyFloat_Check(value))
13288 	    temp = PyFloat_AsDouble(value);
13289 	else
13290 	    temp = PyLong_AsLong(value);
13291 	if ( PyErr_Occurred()!=NULL )
13292 return( -1 );
13293 	sf->design_size = rint(10.0*temp);
13294 	sf->design_range_bottom = 0;
13295 	sf->design_range_top = 0;
13296 	sf->fontstyle_id = 0;
13297 	OtfNameListFree(sf->fontstyle_name);
13298 	sf->fontstyle_name = NULL;
13299 return( 0 );
13300     }
13301 
13302     if ( !PyArg_ParseTuple(value,"dddiO", &temp, &bot, &top, &id, &names )) {
13303         PyErr_Format(PyExc_TypeError,"Expecting 1 or 5 arguments");
13304 return( -1 );
13305     }
13306     sf->design_size = rint(10.0*temp);
13307     sf->design_range_bottom = rint(10.0*bot);
13308     sf->design_range_top = rint(10.0*top);
13309     sf->fontstyle_id = id;
13310 
13311     if ( !PyTuple_Check(names) && !PyList_Check(names)) {
13312 	PyErr_Format(PyExc_TypeError,"Final argument must be a tuple of tuples");
13313 return( -1 );
13314     }
13315     head = last = NULL;
13316     for ( i=0; i<PySequence_Size(names); ++i ) {
13317 	PyObject *subtuple = PySequence_GetItem(names,i);
13318 	PyObject *val;
13319 	int lang;
13320 
13321         if ( (!PyTuple_Check(subtuple) && !PyList_Check(subtuple)) ||
13322 	         (PySequence_Size(subtuple) != 2) ) {
13323 	    PyErr_Format(PyExc_TypeError, "Value must be a tuple of a language name and string" );
13324 	    OtfNameListFree(head);
13325 	    Py_XDECREF(subtuple);
13326 return( -1 );
13327 	}
13328 	val = PySequence_GetItem(subtuple,0);
13329 	if ( PyUnicode_Check(val) ) {
13330 	    if ((string = PyUnicode_AsUTF8(val)) != NULL) {
13331 		lang = FlagsFromString(string,sfnt_name_mslangs,"language");
13332 	    }
13333 	    if (string == NULL || lang == FLAG_UNKNOWN) {
13334 		Py_DECREF(val);
13335 		Py_DECREF(subtuple);
13336 		OtfNameListFree(head);
13337 		return -1;
13338 	    }
13339 	} else if ( PyLong_Check(val)) {
13340 	    lang = PyLong_AsLong(val);
13341 	} else {
13342 	    Py_XDECREF(val);
13343 	    Py_DECREF(subtuple);
13344 	    PyErr_Format(PyExc_TypeError, "Language must be a string or an integer");
13345 	    OtfNameListFree(head);
13346 return( -1 );
13347 	}
13348 	Py_DECREF(val);
13349 	val = PySequence_GetItem(subtuple,1);
13350 	string = copy(PyUnicode_AsUTF8(val));
13351 	Py_XDECREF(val);
13352 	Py_DECREF(subtuple);
13353 	if (string == NULL) {
13354 	    OtfNameListFree(head);
13355 	    PyErr_Format(PyExc_TypeError, "Name must be a string");
13356 	    return -1;
13357 	}
13358 	cur = chunkalloc(sizeof( struct otfname ));
13359 	cur->name = (char*)string; // last assignment to string was a copy
13360 	cur->lang = lang;
13361 	cur->next = NULL;
13362 	if ( head==NULL )
13363 	    head = cur;
13364 	else
13365 	    last->next = cur;
13366 	last = cur;
13367     }
13368     OtfNameListFree(sf->fontstyle_name);
13369     sf->fontstyle_name = head;
13370 
13371 return( 0 );
13372 }
13373 
PyFF_Font_get_cidsubfontcnt(PyFF_Font * self,void * UNUSED (closure))13374 static PyObject *PyFF_Font_get_cidsubfontcnt(PyFF_Font *self, void *UNUSED(closure)) {
13375     if ( CheckIfFontClosed(self) )
13376 return (NULL);
13377     if ( self->fv->cidmaster==NULL )
13378 return( Py_BuildValue("i", 0));
13379 return( Py_BuildValue("i", self->fv->cidmaster->subfontcnt));
13380 }
13381 
PyFF_Font_get_cidsubfontnames(PyFF_Font * self,void * UNUSED (closure))13382 static PyObject *PyFF_Font_get_cidsubfontnames(PyFF_Font *self, void *UNUSED(closure)) {
13383     PyObject *tuple;
13384     SplineFont *cidmaster;
13385     int i;
13386 
13387     if ( CheckIfFontClosed(self) )
13388 return (NULL);
13389     cidmaster = self->fv->cidmaster;
13390     if ( cidmaster==NULL )
13391 Py_RETURN_NONE;
13392     tuple = PyTuple_New(cidmaster->subfontcnt);
13393     for ( i=0; i<cidmaster->subfontcnt; ++i )
13394 	PyTuple_SET_ITEM(tuple,i,Py_BuildValue("s",cidmaster->subfonts[i]->fontname));
13395 return( tuple );
13396 }
13397 
PyFF_Font_get_cidsubfont(PyFF_Font * self,void * UNUSED (closure))13398 static PyObject *PyFF_Font_get_cidsubfont(PyFF_Font *self, void *UNUSED(closure)) {
13399     int i;
13400     SplineFont *cidmaster, *sf;
13401 
13402     if ( CheckIfFontClosed(self) )
13403 return (NULL);
13404     cidmaster = self->fv->cidmaster;
13405     sf = self->fv->sf;
13406     if ( cidmaster==NULL )
13407 return( Py_BuildValue("i", -1));
13408     for ( i=0; i<cidmaster->subfontcnt && sf!=cidmaster->subfonts[i]; ++i );
13409 return( Py_BuildValue("i", i));
13410 }
13411 
PyFF_Font_set_cidsubfont(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13412 static int PyFF_Font_set_cidsubfont(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13413     int i;
13414     SplineFont *cidmaster, *sf;
13415     EncMap *map;
13416 
13417     if ( CheckIfFontClosed(self) )
13418 return (-1);
13419     cidmaster = self->fv->cidmaster;
13420     map = self->fv->map;
13421     if ( cidmaster==NULL ) {
13422 	PyErr_Format(PyExc_EnvironmentError, "Not a cid-keyed font");
13423 return( -1 );
13424     }
13425     if ( PyUnicode_Check(value)) {
13426 	const char *name = PyUnicode_AsUTF8(value);
13427 	if (name == NULL) {
13428 	    return -1;
13429 	}
13430 	for ( i=cidmaster->subfontcnt-1; i>=0 && strcmp(name,cidmaster->subfonts[i]->fontname)!=0; ++i );
13431 	if ( i<0 ) {
13432 	    PyErr_Format(PyExc_EnvironmentError, "No subfont named %s", name);
13433 return( -1 );
13434 	}
13435     } else if ( PyLong_Check(value)) {
13436 	i = PyLong_AsLong(value);
13437 	if ( i<0 || i>=cidmaster->subfontcnt ) {
13438 	    PyErr_Format(PyExc_EnvironmentError, "Subfont index %d out of bounds must be >=0 and <%d.", i, cidmaster->subfontcnt );
13439 return( -1 );
13440 	}
13441     } else {
13442 	PyErr_Format(PyExc_TypeError, "Expected either a string (fontname) or an index when setting the subfont");
13443 return( -1 );
13444     }
13445 
13446     sf = cidmaster->subfonts[i];
13447 
13448     MVDestroyAll(self->fv->sf);
13449     if ( sf->glyphcnt>self->fv->sf->glyphcnt ) {
13450 	free(self->fv->selected);
13451 	self->fv->selected = calloc(sf->glyphcnt,sizeof(char));
13452 	if ( sf->glyphcnt>map->encmax )
13453 	    map->map = realloc(map->map,(map->encmax = sf->glyphcnt)*sizeof(int));
13454 	if ( sf->glyphcnt>map->backmax )
13455 	    map->backmap = realloc(map->backmap,(map->backmax = sf->glyphcnt)*sizeof(int));
13456 	for ( i=0; i<sf->glyphcnt; ++i )
13457 	    map->map[i] = map->backmap[i] = i;
13458 	map->enccount = sf->glyphcnt;
13459     }
13460     self->fv->sf = sf;
13461     if ( !no_windowing_ui ) {
13462 	FVSetTitle(self->fv);
13463 	FontViewReformatOne(self->fv);
13464     }
13465 return( 0 );
13466 }
13467 
PyFF_Font_get_is_cid(PyFF_Font * self,void * UNUSED (closure))13468 static PyObject *PyFF_Font_get_is_cid(PyFF_Font *self, void *UNUSED(closure)) {
13469     SplineFont *cidmaster;
13470 
13471     if ( CheckIfFontClosed(self) )
13472 return (NULL);
13473     cidmaster = self->fv->cidmaster;
13474     if ( cidmaster==NULL )
13475 return( Py_BuildValue("i", 0));
13476 
13477 return( Py_BuildValue("i", 1));
13478 }
13479 
PyFF_Font_get_encoding(PyFF_Font * self,void * UNUSED (closure))13480 static PyObject *PyFF_Font_get_encoding(PyFF_Font *self, void *UNUSED(closure)) {
13481     if ( CheckIfFontClosed(self) )
13482 return (NULL);
13483 return( Py_BuildValue("s", self->fv->map->enc->enc_name));
13484 }
13485 
PyFF_Font_set_encoding(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13486 static int PyFF_Font_set_encoding(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13487     const char *encname;
13488     int ret;
13489 
13490     if ( CheckIfFontClosed(self) )
13491 return (-1);
13492     if ( value==NULL ) {
13493 	PyErr_Format(PyExc_TypeError, "Cannot delete encoding field" );
13494 return( -1 );
13495     } else if ((encname = PyUnicode_AsUTF8(value)) == NULL) {
13496         return -1;
13497     }
13498     ret = SFReencode(self->fv->sf, encname, 0);
13499     if ( ret==-1 )
13500 	PyErr_Format(PyExc_NameError, "Unknown encoding %s", encname);
13501 
13502 return(ret);
13503 }
13504 
13505 /* Not really the right question now... but this is the closest we come */
PyFF_Font_get_is_quadratic(PyFF_Font * self,void * UNUSED (closure))13506 static PyObject *PyFF_Font_get_is_quadratic(PyFF_Font *self, void *UNUSED(closure)) {
13507     if ( CheckIfFontClosed(self) )
13508 return (NULL);
13509 return( Py_BuildValue("i", self->fv->sf->layers[self->fv->active_layer].order2));
13510 }
13511 
PyFF_Font_set_is_quadratic(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13512 static int PyFF_Font_set_is_quadratic(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13513     SplineFont *sf;
13514     int order2;
13515 
13516     if ( CheckIfFontClosed(self) )
13517 return (-1);
13518     sf = self->fv->sf;
13519     if ( value==NULL ) {
13520 	PyErr_Format(PyExc_TypeError, "Cannot delete is_quadratic field" );
13521 return( -1 );
13522     }
13523     order2 = PyLong_AsLong(value);
13524     if ( PyErr_Occurred()!=NULL )
13525 return( -1 );
13526     if ( sf->layers[self->fv->active_layer].order2==order2 )
13527 	/* Do Nothing */;
13528     else if ( order2 ) {
13529 	SFCloseAllInstrs(sf);
13530 	SFConvertToOrder2(sf);
13531     } else
13532 	SFConvertToOrder3(sf);
13533 return(0);
13534 }
13535 
PyFF_Font_get_guide(PyFF_Font * self,void * UNUSED (closure))13536 static PyObject *PyFF_Font_get_guide(PyFF_Font *self, void *UNUSED(closure)) {
13537     if ( CheckIfFontClosed(self) )
13538 return (NULL);
13539 return( (PyObject *) LayerFromLayer(&self->fv->sf->grid,NULL));
13540 }
13541 
PyFF_Font_set_guide(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13542 static int PyFF_Font_set_guide(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13543     SplineSet *ss = NULL, *newss;
13544     int isquad = false;
13545     SplineFont *sf;
13546     Layer *guide;
13547 
13548     if ( CheckIfFontClosed(self) )
13549 return (-1);
13550     if ( value==NULL ) {
13551 	PyErr_Format(PyExc_TypeError, "Cannot delete guide field" );
13552 return( -1 );
13553     }
13554     if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(value)) ) {
13555 	isquad = ((PyFF_Layer *) value)->is_quadratic;
13556 	ss = SSFromLayer( (PyFF_Layer *) value);
13557     } else if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(value)) ) {
13558 	isquad = ((PyFF_Contour *) value)->is_quadratic;
13559 	ss = SSFromContour( (PyFF_Contour *) value,NULL);
13560     } else {
13561 	PyErr_Format( PyExc_TypeError, "Unexpected type" );
13562 return( -1 );
13563     }
13564     if ( PyErr_Occurred() != NULL ) {
13565         SplinePointListsFree(ss);
13566 	return( -1 );
13567     }
13568     sf = self->fv->sf;
13569     guide = &sf->grid;
13570     SplinePointListsFree(guide->splines);
13571     if ( sf->grid.order2!=isquad ) {
13572 	if ( sf->grid.order2 )
13573 	    newss = SplineSetsTTFApprox(ss);
13574 	else
13575 	    newss = SplineSetsPSApprox(ss);
13576 	SplinePointListsFree(ss);
13577 	ss = newss;
13578     }
13579     guide->splines = ss;
13580 return( 0 );
13581 }
13582 
PyFF_Font_get_mark_classes(PyFF_Font * self,void * UNUSED (closure))13583 static PyObject *PyFF_Font_get_mark_classes(PyFF_Font *self, void *UNUSED(closure)) {
13584     PyObject *tuple, *nametuple;
13585     SplineFont *sf;
13586     int i;
13587 
13588     if ( CheckIfFontClosed(self) )
13589 return (NULL);
13590     sf = self->fv->sf;
13591     if ( sf->mark_class_cnt==0 )
13592 Py_RETURN_NONE;
13593 
13594     tuple = PyTuple_New(sf->mark_class_cnt-1);
13595     for ( i=1; i<sf->mark_class_cnt; ++i ) {
13596 	nametuple = TupleOfGlyphNames(sf->mark_classes[i],0);
13597 	PyTuple_SetItem(tuple,i-1,Py_BuildValue("(sO)", sf->mark_class_names[i], nametuple));
13598     }
13599 return( tuple );
13600 }
13601 
PyFF_Font_set_mark_classes(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13602 static int PyFF_Font_set_mark_classes(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13603     SplineFont *sf;
13604     int i, cnt;
13605     char **names, **classes;
13606     char *nm;
13607     PyObject *subtuple;
13608 
13609     if ( CheckIfFontClosed(self) )
13610         return (-1);
13611     sf = self->fv->sf;
13612     if ( value==NULL || value==Py_None ) {
13613         /* del font.markClasses  or  font.markClasses=None  removes old values */
13614         cnt = 0;
13615     } else if ( !PyTuple_Check(value) && !PyList_Check(value)) {
13616         PyErr_Format(PyExc_TypeError,"Expecting a tuple of tuples of names and glyphs");
13617         return( -1 );
13618     } else {
13619         /* we may get too many tuples, or too few (an empty tuple), or an error */
13620         cnt = PySequence_Size(value);
13621         if ( cnt==-1 )
13622             return( -1 );
13623         if ( cnt>=256 ) {
13624             PyErr_Format(PyExc_ValueError, "There may be at most 255 mark classes" );
13625             return( -1 );
13626         }
13627     }
13628     if ( cnt==0 ) {
13629         /* markClasses is being removed, delete any old, reset counts and pointers */
13630 	MarkClassFree(sf->mark_class_cnt,sf->mark_classes,sf->mark_class_names);
13631 	sf->mark_class_cnt = 0;
13632 	sf->mark_classes = NULL;
13633 	sf->mark_class_names = NULL;
13634         return( 0 );
13635     }
13636 
13637     names = malloc((cnt+1)*sizeof(char *));
13638     classes = malloc((cnt+1)*sizeof(char *));
13639     names[0] = classes[0] = NULL;
13640     /* Fill in names[] and classes[], starting at index 1 instead of 0 */
13641     for ( i=1; i<=cnt; ++i ) {
13642 	names[i] = classes[i] = NULL;
13643         PyObject *seqItem = PySequence_GetItem(value,i-1);
13644 	if ( !PyArg_ParseTuple(seqItem,"sO", &nm, &subtuple)) {
13645             PyErr_Format(PyExc_TypeError,"Expecting inner tuples to be name and glyphs");
13646             Py_DECREF(seqItem);
13647 	    FreeStringArray( i, names );
13648 	    FreeStringArray( i, classes );
13649             return( -1 );
13650 	}
13651         Py_DECREF(seqItem);
13652         if ( strlen(nm)==0 ) {
13653             PyErr_Format(PyExc_TypeError,"Mark class name strings may not be empty");
13654             FreeStringArray( i, names );
13655             FreeStringArray( i, classes );
13656             return( -1 );
13657         }
13658 	classes[i] = GlyphNamesFromTuple(subtuple);
13659 	if ( classes[i]==NULL ) {
13660 	    FreeStringArray( i, names );
13661 	    FreeStringArray( i, classes );
13662             return( -1 );
13663 	}
13664 	names[i] = copy(nm);
13665     }
13666 
13667     MarkClassFree(sf->mark_class_cnt,sf->mark_classes,sf->mark_class_names);
13668     sf->mark_class_cnt = cnt+1; /* +1 because index 0 was skipped */
13669     sf->mark_classes = classes;
13670     sf->mark_class_names = names;
13671 
13672 return( 0 );
13673 }
13674 
PyFF_Font_get_em(PyFF_Font * self,void * UNUSED (closure))13675 static PyObject *PyFF_Font_get_em(PyFF_Font *self, void *UNUSED(closure)) {
13676     if ( CheckIfFontClosed(self) )
13677 return (NULL);
13678 return( Py_BuildValue("i", self->fv->sf->ascent + self->fv->sf->descent ));
13679 }
13680 
PyFF_Font_set_em(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13681 static int PyFF_Font_set_em(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13682     int newem, as, ds, oldem;
13683     SplineFont *sf;
13684 
13685     if ( CheckIfFontClosed(self) )
13686 return (-1);
13687     if ( value==NULL ) {
13688 	PyErr_Format(PyExc_TypeError, "Cannot delete em field" );
13689 return( -1 );
13690     }
13691     if ( !PyLong_Check(value)) {
13692 	PyErr_Format( PyExc_TypeError, "Unexpected type" );
13693 return( -1 );
13694     }
13695     newem = PyLong_AsLong(value);
13696     if ( newem<10 || newem>=16*1024 ) {
13697 	PyErr_Format(PyExc_ValueError, "Em size too big or too small" );
13698 return( -1 );
13699     }
13700     sf = self->fv->sf;
13701     if ( (oldem = sf->ascent+sf->descent)<=0 ) oldem = 1;
13702     ds = rint( (double) newem * sf->descent / oldem );
13703     as = newem - ds;
13704     SFScaleToEm(sf,as,ds);
13705 return( 0 );
13706 }
13707 
PyFF_Font_SetMaxpValue(PyFF_Font * self,PyObject * value,const char * str)13708 static int PyFF_Font_SetMaxpValue(PyFF_Font *self,PyObject *value, const char *str) {
13709     SplineFont *sf;
13710     struct ttf_table *tab;
13711     int val;
13712 
13713     if ( CheckIfFontClosed(self) )
13714 return (-1);
13715     sf = self->fv->sf;
13716     val = PyLong_AsLong(value);
13717     if ( PyErr_Occurred())
13718 return( -1 );
13719 
13720     tab = SFFindTable(sf,CHR('m','a','x','p'));
13721     if ( tab==NULL ) {
13722 	tab = chunkalloc(sizeof(struct ttf_table));
13723 	tab->next = sf->ttf_tables;
13724 	sf->ttf_tables = tab;
13725 	tab->tag = CHR('m','a','x','p');
13726     }
13727     if ( tab->len<32 ) {
13728 	tab->data = realloc(tab->data,32);
13729 	memset(tab->data+tab->len,0,32-tab->len);
13730 	if ( tab->len<16 )
13731 	    tab->data[15] = 2;			/* Default zones to 2 */
13732 	tab->len = tab->maxlen = 32;
13733     }
13734     if ( strmatch(str,"Zones")==0 )
13735 	memputshort(tab->data,7*sizeof(uint16),val);
13736     else if ( strmatch(str,"TwilightPntCnt")==0 )
13737 	memputshort(tab->data,8*sizeof(uint16),val);
13738     else if ( strmatch(str,"StorageCnt")==0 )
13739 	memputshort(tab->data,9*sizeof(uint16),val);
13740     else if ( strmatch(str,"MaxStackDepth")==0 )
13741 	memputshort(tab->data,12*sizeof(uint16),val);
13742     else if ( strmatch(str,"FDEFs")==0 )
13743 	memputshort(tab->data,10*sizeof(uint16),val);
13744     else if ( strmatch(str,"IDEFs")==0 )
13745 	memputshort(tab->data,11*sizeof(uint16),val);
13746 return( 0 );
13747 }
13748 
PyFF_Font_GetMaxpValue(PyFF_Font * self,const char * str)13749 static PyObject *PyFF_Font_GetMaxpValue(PyFF_Font *self,const char *str) {
13750     SplineFont *sf;
13751     struct ttf_table *tab;
13752     uint8 *data, dummy[32];
13753     int val;
13754 
13755     if ( CheckIfFontClosed(self) )
13756 return (NULL);
13757     sf = self->fv->sf;
13758     memset(dummy,0,sizeof(dummy));
13759     dummy[15] = 2;
13760     tab = SFFindTable(sf,CHR('m','a','x','p'));
13761     if ( tab==NULL )
13762 	data = dummy;
13763     else if ( tab->len<32 ) {
13764 	memcpy(dummy,tab->data,tab->len);
13765 	data = dummy;
13766     } else
13767 	data = tab->data;
13768 
13769     if ( strmatch(str,"Zones")==0 )
13770 	val = memushort(data,32,7*sizeof(uint16));
13771     else if ( strmatch(str,"TwilightPntCnt")==0 )
13772 	val = memushort(data,32,8*sizeof(uint16));
13773     else if ( strmatch(str,"StorageCnt")==0 )
13774 	val = memushort(data,32,9*sizeof(uint16));
13775     else if ( strmatch(str,"MaxStackDepth")==0 )
13776 	val = memushort(data,32,12*sizeof(uint16));
13777     else if ( strmatch(str,"FDEFs")==0 )
13778 	val = memushort(data,32,10*sizeof(uint16));
13779     else if ( strmatch(str,"IDEFs")==0 )
13780 	val = memushort(data,32,11*sizeof(uint16));
13781     else
13782 	val = -1;
13783 return( Py_BuildValue("i",val));
13784 }
13785 
PyFF_Font_get_maxp_IDEFs(PyFF_Font * self,void * UNUSED (closure))13786 static PyObject *PyFF_Font_get_maxp_IDEFs(PyFF_Font *self, void *UNUSED(closure)) {
13787     if ( CheckIfFontClosed(self) )
13788 return (NULL);
13789 return( PyFF_Font_GetMaxpValue(self,"IDEFs"));
13790 }
13791 
PyFF_Font_set_maxp_IDEFs(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13792 static int PyFF_Font_set_maxp_IDEFs(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13793     if ( CheckIfFontClosed(self) )
13794 return (-1);
13795 return( PyFF_Font_SetMaxpValue(self,value,"IDEFs"));
13796 }
13797 
PyFF_Font_get_maxp_FDEFs(PyFF_Font * self,void * UNUSED (closure))13798 static PyObject *PyFF_Font_get_maxp_FDEFs(PyFF_Font *self, void *UNUSED(closure)) {
13799     if ( CheckIfFontClosed(self) )
13800 return (NULL);
13801 return( PyFF_Font_GetMaxpValue(self,"FDEFs"));
13802 }
13803 
PyFF_Font_set_maxp_FDEFs(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13804 static int PyFF_Font_set_maxp_FDEFs(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13805     if ( CheckIfFontClosed(self) )
13806 return (-1);
13807 return( PyFF_Font_SetMaxpValue(self,value,"FDEFs"));
13808 }
13809 
PyFF_Font_get_maxp_maxStackDepth(PyFF_Font * self,void * UNUSED (closure))13810 static PyObject *PyFF_Font_get_maxp_maxStackDepth(PyFF_Font *self, void *UNUSED(closure)) {
13811     if ( CheckIfFontClosed(self) )
13812 return (NULL);
13813 return( PyFF_Font_GetMaxpValue(self,"MaxStackDepth"));
13814 }
13815 
PyFF_Font_set_maxp_maxStackDepth(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13816 static int PyFF_Font_set_maxp_maxStackDepth(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13817     if ( CheckIfFontClosed(self) )
13818 return (-1);
13819 return( PyFF_Font_SetMaxpValue(self,value,"MaxStackDepth"));
13820 }
13821 
PyFF_Font_get_maxp_storageCnt(PyFF_Font * self,void * UNUSED (closure))13822 static PyObject *PyFF_Font_get_maxp_storageCnt(PyFF_Font *self, void *UNUSED(closure)) {
13823     if ( CheckIfFontClosed(self) )
13824 return (NULL);
13825 return( PyFF_Font_GetMaxpValue(self,"StorageCnt"));
13826 }
13827 
PyFF_Font_set_maxp_storageCnt(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13828 static int PyFF_Font_set_maxp_storageCnt(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13829     if ( CheckIfFontClosed(self) )
13830 return (-1);
13831 return( PyFF_Font_SetMaxpValue(self,value,"StorageCnt"));
13832 }
13833 
PyFF_Font_get_maxp_twilightPtCnt(PyFF_Font * self,void * UNUSED (closure))13834 static PyObject *PyFF_Font_get_maxp_twilightPtCnt(PyFF_Font *self, void *UNUSED(closure)) {
13835     if ( CheckIfFontClosed(self) )
13836 return (NULL);
13837 return( PyFF_Font_GetMaxpValue(self,"TwilightPntCnt"));
13838 }
13839 
PyFF_Font_set_maxp_twilightPtCnt(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13840 static int PyFF_Font_set_maxp_twilightPtCnt(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13841     if ( CheckIfFontClosed(self) )
13842 return (-1);
13843 return( PyFF_Font_SetMaxpValue(self,value,"TwilightPntCnt"));
13844 }
13845 
PyFF_Font_get_maxp_zones(PyFF_Font * self,void * UNUSED (closure))13846 static PyObject *PyFF_Font_get_maxp_zones(PyFF_Font *self, void *UNUSED(closure)) {
13847     if ( CheckIfFontClosed(self) )
13848 return (NULL);
13849 return( PyFF_Font_GetMaxpValue(self,"Zones"));
13850 }
13851 
PyFF_Font_set_maxp_zones(PyFF_Font * self,PyObject * value,void * UNUSED (closure))13852 static int PyFF_Font_set_maxp_zones(PyFF_Font *self,PyObject *value, void *UNUSED(closure)) {
13853     if ( CheckIfFontClosed(self) )
13854 return (-1);
13855 return( PyFF_Font_SetMaxpValue(self,value,"Zones"));
13856 }
13857 
PyFF_Font_get_xHeight(PyFF_Font * self,void * UNUSED (closure))13858 static PyObject *PyFF_Font_get_xHeight(PyFF_Font *self, void *UNUSED(closure)) {
13859     SplineFont *sf;
13860     double val;
13861     if ( CheckIfFontClosed(self) )
13862 return (NULL);
13863     sf = self->fv->sf;
13864     val = SFXHeight(sf,self->fv->active_layer,true);
13865 return( Py_BuildValue("d",val));
13866 }
13867 
PyFF_Font_get_capHeight(PyFF_Font * self,void * UNUSED (closure))13868 static PyObject *PyFF_Font_get_capHeight(PyFF_Font *self, void *UNUSED(closure)) {
13869     SplineFont *sf;
13870     double val;
13871     if ( CheckIfFontClosed(self) )
13872 return (NULL);
13873     sf = self->fv->sf;
13874     val = SFCapHeight(sf,self->fv->active_layer,true);
13875 return( Py_BuildValue("d",val));
13876 }
13877 
13878 static PyGetSetDef PyFF_Font_getset[] = {
13879     {(char *)"userdata",
13880      (getter)PyFF_Font_get_temporary, (setter)PyFF_Font_set_temporary,
13881      (char *)"arbitrary (non-persistent) user data (deprecated name for temporary)", NULL},
13882     {(char *)"temporary",
13883      (getter)PyFF_Font_get_temporary, (setter)PyFF_Font_set_temporary,
13884      (char *)"arbitrary (non-persistent) user data", NULL},
13885     {(char *)"persistant",		/* I documented this member with the wrong spelling... so support it */
13886      (getter)PyFF_Font_get_persistent, (setter)PyFF_Font_set_persistent,
13887      (char *)"arbitrary persistent user data", NULL},
13888     {(char *)"persistent",
13889      (getter)PyFF_Font_get_persistent, (setter)PyFF_Font_set_persistent,
13890      (char *)"arbitrary persistent user data", NULL},
13891     {(char *)"selection",
13892      (getter)PyFF_Font_get_selection, (setter)PyFF_Font_set_selection,
13893      (char *)"The font's selection array", NULL},
13894     {(char *)"activeLayer",
13895      (getter)PyFF_Font_get_activeLayer, (setter)PyFF_Font_set_activeLayer,
13896      (char *)"The currently active layer in the font", NULL},
13897     {(char *)"sfnt_names",
13898      (getter)PyFF_Font_get_sfntnames, (setter)PyFF_Font_set_sfntnames,
13899      (char *)"The sfnt 'name' table. A tuple of all ms names.\nEach name is itself a tuple of strings (language,strid,name)\nMac names will be automagically created from ms names", NULL},
13900     {(char *)"bitmapSizes",
13901      (getter)PyFF_Font_get_bitmapSizes, (setter)PyFF_Font_set_bitmapSizes,
13902      (char *)"A tuple of sizes of all bitmaps associated with the font", NULL},
13903     {(char *)"gpos_lookups",
13904      (getter)PyFF_Font_get_gpos_lookups, NULL,
13905      (char *)"The names of all lookups in the font's GPOS table (readonly)", NULL},
13906     {(char *)"gsub_lookups",
13907      (getter)PyFF_Font_get_gsub_lookups, NULL,
13908      (char *)"The names of all lookups in the font's GSUB table (readonly)", NULL},
13909     {(char *)"horizontalBaseline",
13910      (getter)PyFF_Font_get_horizontal_baseline, (setter)PyFF_Font_set_horizontal_baseline,
13911      (char *)"Horizontal baseline data, if any", NULL},
13912     {(char *)"verticalBaseline",
13913      (getter)PyFF_Font_get_vertical_baseline, (setter)PyFF_Font_set_vertical_baseline,
13914      (char *)"Vertical baseline data, if any", NULL},
13915     {(char *)"private",
13916      (getter)PyFF_Font_get_private, NULL,
13917      (char *)"The font's PostScript private dictionary (You may not set this field, but you may set things in it)", NULL},
13918     {(char *)"math",
13919      (getter)PyFF_Font_get_math, NULL,
13920      (char *)"The font's math constants (You may not set this field, but you may set things in it)", NULL},
13921     {(char *)"texparameters",
13922      (getter)PyFF_Font_get_texparams, NULL,
13923      (char *)"The font's TeX font parameters", NULL},
13924     {(char *)"cvt",
13925      (getter)PyFF_Font_get_cvt, (setter)PyFF_Font_set_cvt,
13926      (char *)"The font's TrueType cvt table", NULL},
13927     {(char *)"path",
13928      (getter)PyFF_Font_get_path, NULL,
13929      (char *)"filename of the original font file loaded (readonly)", NULL},
13930     {(char *)"sfd_path",
13931      (getter)PyFF_Font_get_sfd_path, NULL,
13932      (char *)"filename of the sfd file containing this font (if any) (readonly)", NULL},
13933     {(char *)"default_base_filename",
13934      (getter)PyFF_Font_get_defbasefilename, (setter)PyFF_Font_set_defbasefilename,
13935      (char *)"The default base for the filename when generating a font", NULL},
13936     {(char *)"fontname",
13937      (getter)PyFF_Font_get_fontname, (setter)PyFF_Font_set_fontname,
13938      (char *)"font name", NULL},
13939     {(char *)"fullname",
13940      (getter)PyFF_Font_get_fullname, (setter)PyFF_Font_set_fullname,
13941      (char *)"full name", NULL},
13942     {(char *)"familyname",
13943      (getter)PyFF_Font_get_familyname, (setter)PyFF_Font_set_familyname,
13944      (char *)"family name", NULL},
13945     {(char *)"weight",
13946      (getter)PyFF_Font_get_weight, (setter)PyFF_Font_set_weight,
13947      (char *)"weight (PS)", NULL},
13948     {(char *)"copyright",
13949      (getter)PyFF_Font_get_copyright, (setter)PyFF_Font_set_copyright,
13950      (char *)"copyright (PS)", NULL},
13951     {(char *)"version",
13952      (getter)PyFF_Font_get_version, (setter)PyFF_Font_set_version,
13953      (char *)"font version (PS)", NULL},
13954     {(char *)"comment",
13955      (getter)PyFF_Font_get_comments, (setter)PyFF_Font_set_comments,
13956      (char *)"A comment associated with the font. Can be anything", NULL},
13957     {(char *)"fontlog",
13958      (getter)PyFF_Font_get_fontlog, (setter)PyFF_Font_set_fontlog,
13959      (char *)"A comment associated with the font. Can be anything", NULL},
13960     {(char *)"xuid",
13961      (getter)PyFF_Font_get_xuid, (setter)PyFF_Font_set_xuid,
13962      (char *)"PostScript eXtended Unique ID", NULL},
13963     {(char *)"fondname",
13964      (getter)PyFF_Font_get_fondname, (setter)PyFF_Font_set_fondname,
13965      (char *)"Mac FOND resource name", NULL},
13966     {(char *)"cidregistry",
13967      (getter)PyFF_Font_get_cidcidregistry, (setter)PyFF_Font_set_cidcidregistry,
13968      (char *)"CID Registry", NULL},
13969     {(char *)"cidordering",
13970      (getter)PyFF_Font_get_cidordering, (setter)PyFF_Font_set_cidordering,
13971      (char *)"CID Ordering", NULL},
13972     {(char *)"cidsupplement",
13973      (getter)PyFF_Font_get_supplement, (setter)PyFF_Font_set_supplement,
13974      (char *)"CID Supplement", NULL},
13975     {(char *)"cidsubfont",
13976      (getter)PyFF_Font_get_cidsubfont, (setter)PyFF_Font_set_cidsubfont,
13977      (char *)"Returns the index of the current subfont of a cid-keyed font (or -1), and allows you to change the subfont.", NULL},
13978     {(char *)"cidsubfontcnt",
13979      (getter)PyFF_Font_get_cidsubfontcnt, NULL,
13980      (char *)"The number of sub fonts that make up a CID keyed font (readonly)", NULL},
13981     {(char *)"cidsubfontnames",
13982      (getter)PyFF_Font_get_cidsubfontnames, NULL,
13983      (char *)"The names of all the sub fonts that make up a CID keyed font (readonly)", NULL},
13984     {(char *)"cidfontname",
13985      (getter)PyFF_Font_get_cidfontname, (setter)PyFF_Font_set_cidfontname,
13986      (char *)"Font name of the cid-keyed font as a whole.", NULL},
13987     {(char *)"cidfamilyname",
13988      (getter)PyFF_Font_get_cidfamilyname, (setter)PyFF_Font_set_cidfamilyname,
13989      (char *)"Family name of the cid-keyed font as a whole.", NULL},
13990     {(char *)"cidfullname",
13991      (getter)PyFF_Font_get_cidfullname, (setter)PyFF_Font_set_cidfullname,
13992      (char *)"Full name of the cid-keyed font as a whole.", NULL},
13993     {(char *)"cidweight",
13994      (getter)PyFF_Font_get_cidweight, (setter)PyFF_Font_set_cidweight,
13995      (char *)"Weight of the cid-keyed font as a whole.", NULL},
13996     {(char *)"cidcopyright",
13997      (getter)PyFF_Font_get_cidcopyright, (setter)PyFF_Font_set_cidcopyright,
13998      (char *)"Copyright message of the cid-keyed font as a whole.", NULL},
13999     {(char *)"cidversion",
14000      (getter)PyFF_Font_get_cidversion, (setter)PyFF_Font_set_cidversion,
14001      (char *)"CID Version", NULL},
14002     {(char *)"iscid",		/* This should be is_cid, but due to an early misprint I now include both */
14003      (getter)PyFF_Font_get_is_cid, NULL,
14004      (char *)"Whether the font is a cid-keyed font. (readonly)", NULL},
14005     {(char *)"is_cid",
14006      (getter)PyFF_Font_get_is_cid, NULL,
14007      (char *)"Whether the font is a cid-keyed font. (readonly)", NULL},
14008     {(char *)"italicangle",
14009      (getter)PyFF_Font_get_italicangle, (setter)PyFF_Font_set_italicangle,
14010      (char *)"The Italic angle (skewedness) of the font", NULL},
14011     {(char *)"creationtime",
14012      (getter) PyFF_Font_get_creationtime, NULL,
14013      (char*)"Font creation time. (readonly)", NULL},
14014     {(char *)"upos",
14015      (getter)PyFF_Font_get_upos, (setter)PyFF_Font_set_upos,
14016      (char *)"Underline Position", NULL},
14017     {(char *)"uwidth",
14018      (getter)PyFF_Font_get_uwidth, (setter)PyFF_Font_set_uwidth,
14019      (char *)"Underline Width", NULL},
14020     {(char *)"strokewidth",
14021      (getter)PyFF_Font_get_strokewidth, (setter)PyFF_Font_set_strokewidth,
14022      (char *)"Stroke Width", NULL},
14023     {(char *)"ascent",
14024      (getter)PyFF_Font_get_ascent, (setter)PyFF_Font_set_ascent,
14025      (char *)"Font Ascent", NULL},
14026     {(char *)"descent",
14027      (getter)PyFF_Font_get_descent, (setter)PyFF_Font_set_descent,
14028      (char *)"Font Descent", NULL},
14029     {(char *)"xHeight",
14030      (getter)PyFF_Font_get_xHeight, NULL,
14031      (char *)"X Height of font (negative number means could not be computed (ie. no lowercase glyphs))", NULL},
14032     {(char *)"capHeight",
14033      (getter)PyFF_Font_get_capHeight, NULL,
14034      (char *)"Cap Height of font (negative number means could not be computed (ie. no uppercase glyphs))", NULL},
14035     {(char *)"em",
14036      (getter)PyFF_Font_get_em, (setter)PyFF_Font_set_em,
14037      (char *)"Em size", NULL},
14038     {(char *)"sfntRevision",
14039      (getter)PyFF_Font_get_sfntRevision, (setter)PyFF_Font_set_sfntRevision,
14040      (char *)"sfnt revision number", NULL},
14041     {(char *)"woffMajor",
14042      (getter)PyFF_Font_get_woffMajor, (setter)PyFF_Font_set_woffMajor,
14043      (char *)"woff major version number", NULL},
14044     {(char *)"woffMinor",
14045      (getter)PyFF_Font_get_woffMinor, (setter)PyFF_Font_set_woffMinor,
14046      (char *)"woff minor version number", NULL},
14047     {(char *)"woffMetadata",
14048      (getter)PyFF_Font_get_woffMetadata, (setter)PyFF_Font_set_woffMetadata,
14049      (char *)"woff meta data string (unparsed xml)", NULL},
14050     {(char *)"vertical_origin",
14051      (getter)PyFF_Font_get_vertical_origin, (setter)PyFF_Font_set_vertical_origin,
14052      (char *)"Vertical Origin (No longer supported, use BASE table instead)", NULL},
14053     {(char *)"uniqueid",
14054      (getter)PyFF_Font_get_uniqueid, (setter)PyFF_Font_set_uniqueid,
14055      (char *)"PostScript Unique ID", NULL},
14056     {(char *)"layer_cnt",
14057      (getter)PyFF_Font_get_layer_cnt, NULL,
14058      (char *)"Returns the number of layers in the font (readonly)", NULL},
14059     {(char *)"layers",
14060      (getter)PyFF_Font_get_layers, NULL,
14061      (char *)"Returns a dictionary like object with information on the layers of the font", NULL},
14062     {(char *)"loadState",
14063      (getter)PyFF_Font_get_loadvalidation_state, NULL,
14064      (char *)"A bitmask indicating non-fatal errors found when loading the font (readonly)", NULL},
14065     {(char *)"privateState",
14066      (getter)PyFF_Font_get_privatevalidation_state, NULL,
14067      (char *)"A bitmask indicating errors in the (PostScript) Private dictionary", NULL},
14068     {(char *)"macstyle",
14069      (getter)PyFF_Font_get_macstyle, (setter)PyFF_Font_set_macstyle,
14070      (char *)"Mac Style Bits", NULL},
14071     {(char *)"design_size",
14072      (getter)PyFF_Font_get_design_size, (setter)PyFF_Font_set_design_size,
14073      (char *)"Point size for which this font was designed", NULL},
14074     {(char *)"size_feature",
14075      (getter)PyFF_Font_get_size_feature, (setter)PyFF_Font_set_size_feature,
14076      (char *)"A tuple containing the info needed for the 'size' feature", NULL},
14077     {(char *)"gasp_version",
14078      (getter)PyFF_Font_get_gasp_version, (setter)PyFF_Font_set_gasp_version,
14079      (char *)"Gasp table version number", NULL},
14080     {(char *)"gasp",
14081      (getter)PyFF_Font_get_gasp, (setter)PyFF_Font_set_gasp,
14082      (char *)"Gasp table as a tuple", NULL},
14083     {(char *)"maxp_zones",
14084      (getter)PyFF_Font_get_maxp_zones, (setter)PyFF_Font_set_maxp_zones,
14085      (char *)"The number of zones used in the tt program", NULL},
14086     {(char *)"maxp_twilightPtCnt",
14087      (getter)PyFF_Font_get_maxp_twilightPtCnt, (setter)PyFF_Font_set_maxp_twilightPtCnt,
14088      (char *)"The number of points in the twilight zone of the tt program", NULL},
14089     {(char *)"maxp_storageCnt",
14090      (getter)PyFF_Font_get_maxp_storageCnt, (setter)PyFF_Font_set_maxp_storageCnt,
14091      (char *)"The number of storage locations used by the tt program", NULL},
14092     {(char *)"maxp_maxStackDepth",
14093      (getter)PyFF_Font_get_maxp_maxStackDepth, (setter)PyFF_Font_set_maxp_maxStackDepth,
14094      (char *)"The maximum stack depth used by the tt program", NULL},
14095     {(char *)"maxp_FDEFs",
14096      (getter)PyFF_Font_get_maxp_FDEFs, (setter)PyFF_Font_set_maxp_FDEFs,
14097      (char *)"The number of function definitions used by the tt program", NULL},
14098     {(char *)"maxp_IDEFs",
14099      (getter)PyFF_Font_get_maxp_IDEFs, (setter)PyFF_Font_set_maxp_IDEFs,
14100      (char *)"The number of instruction definitions used by the tt program", NULL},
14101     {(char *)"os2_version",
14102      (getter)PyFF_Font_get_os2_version, (setter)PyFF_Font_set_os2_version,
14103      (char *)"OS/2 table version number", NULL},
14104     {(char *)"os2_weight",
14105      (getter)PyFF_Font_get_OS2_weight, (setter)PyFF_Font_set_OS2_weight,
14106      (char *)"OS/2 weight", NULL},
14107     {(char *)"os2_width",
14108      (getter)PyFF_Font_get_OS2_width, (setter)PyFF_Font_set_OS2_width,
14109      (char *)"OS/2 width", NULL},
14110     {(char *)"os2_stylemap",
14111      (getter)PyFF_Font_get_OS2_stylemap, (setter)PyFF_Font_set_OS2_stylemap,
14112      (char *)"OS/2 stylemap", NULL},
14113     {(char *)"os2_fstype",
14114      (getter)PyFF_Font_get_OS2_fstype, (setter)PyFF_Font_set_OS2_fstype,
14115      (char *)"OS/2 fstype", NULL},
14116     {(char *)"head_optimized_for_cleartype",
14117      (getter)PyFF_Font_get_head_optimized_for_cleartype, (setter)PyFF_Font_set_head_optimized_for_cleartype,
14118      (char *)"Whether the font is optimized for cleartype", NULL},
14119     {(char *)"hhea_linegap",
14120      (getter)PyFF_Font_get_OS2_linegap, (setter)PyFF_Font_set_OS2_linegap,
14121      (char *)"hhea linegap", NULL},
14122     {(char *)"hhea_ascent",
14123      (getter)PyFF_Font_get_OS2_hhead_ascent, (setter)PyFF_Font_set_OS2_hhead_ascent,
14124      (char *)"hhea ascent", NULL},
14125     {(char *)"hhea_descent",
14126      (getter)PyFF_Font_get_OS2_hhead_descent, (setter)PyFF_Font_set_OS2_hhead_descent,
14127      (char *)"hhea descent", NULL},
14128     {(char *)"hhea_ascent_add",
14129      (getter)PyFF_Font_get_OS2_hheadascent_add, (setter)PyFF_Font_set_OS2_hheadascent_add,
14130      (char *)"Whether the hhea_ascent field is used as is, or as an offset applied to the value FontForge thinks appropriate", NULL},
14131     {(char *)"hhea_descent_add",
14132      (getter)PyFF_Font_get_OS2_hheaddescent_add, (setter)PyFF_Font_set_OS2_hheaddescent_add,
14133      (char *)"Whether the hhea_descent field is used as is, or as an offset applied to the value FontForge thinks appropriate", NULL},
14134     {(char *)"vhea_linegap",
14135      (getter)PyFF_Font_get_OS2_vlinegap, (setter)PyFF_Font_set_OS2_vlinegap,
14136      (char *)"vhea linegap", NULL},
14137     {(char *)"os2_typoascent",
14138      (getter)PyFF_Font_get_OS2_os2_typoascent, (setter)PyFF_Font_set_OS2_os2_typoascent,
14139      (char *)"OS/2 Typographic Ascent", NULL},
14140     {(char *)"os2_typodescent",
14141      (getter)PyFF_Font_get_OS2_os2_typodescent, (setter)PyFF_Font_set_OS2_os2_typodescent,
14142      (char *)"OS/2 Typographic Descent", NULL},
14143     {(char *)"os2_typolinegap",
14144      (getter)PyFF_Font_get_OS2_os2_typolinegap, (setter)PyFF_Font_set_OS2_os2_typolinegap,
14145      (char *)"OS/2 Typographic Linegap", NULL},
14146     {(char *)"os2_typoascent_add",
14147      (getter)PyFF_Font_get_OS2_typoascent_add, (setter)PyFF_Font_set_OS2_typoascent_add,
14148      (char *)"Whether the os2_typoascent field is used as is, or as an offset applied to the value FontForge thinks appropriate", NULL},
14149     {(char *)"os2_typodescent_add",
14150      (getter)PyFF_Font_get_OS2_typodescent_add, (setter)PyFF_Font_set_OS2_typodescent_add,
14151      (char *)"Whether the os2_typodescent field is used as is, or as an offset applied to the value FontForge thinks appropriate", NULL},
14152     {(char *)"os2_winascent",
14153      (getter)PyFF_Font_get_OS2_os2_winascent, (setter)PyFF_Font_set_OS2_os2_winascent,
14154      (char *)"OS/2 Windows Ascent", NULL},
14155     {(char *)"os2_windescent",
14156      (getter)PyFF_Font_get_OS2_os2_windescent, (setter)PyFF_Font_set_OS2_os2_windescent,
14157      (char *)"OS/2 Windows Descent", NULL},
14158     {(char *)"os2_winascent_add",
14159      (getter)PyFF_Font_get_OS2_winascent_add, (setter)PyFF_Font_set_OS2_winascent_add,
14160      (char *)"Whether the os2_winascent field is used as is, or as an offset applied to the value FontForge thinks appropriate", NULL},
14161     {(char *)"os2_windescent_add",
14162      (getter)PyFF_Font_get_OS2_windescent_add, (setter)PyFF_Font_set_OS2_windescent_add,
14163      (char *)"Whether the os2_windescent field is used as is, or as an offset applied to the value FontForge thinks appropriate", NULL},
14164     {(char *)"os2_subxsize",
14165      (getter)PyFF_Font_get_OS2_os2_subxsize, (setter)PyFF_Font_set_OS2_os2_subxsize,
14166      (char *)"OS/2 Subscript XSize", NULL},
14167     {(char *)"os2_subxoff",
14168      (getter)PyFF_Font_get_OS2_os2_subxoff, (setter)PyFF_Font_set_OS2_os2_subxoff,
14169      (char *)"OS/2 Subscript XOffset", NULL},
14170     {(char *)"os2_subysize",
14171      (getter)PyFF_Font_get_OS2_os2_subysize, (setter)PyFF_Font_set_OS2_os2_subysize,
14172      (char *)"OS/2 Subscript YSize", NULL},
14173     {(char *)"os2_subyoff",
14174      (getter)PyFF_Font_get_OS2_os2_subyoff, (setter)PyFF_Font_set_OS2_os2_subyoff,
14175      (char *)"OS/2 Subscript YOffset", NULL},
14176     {(char *)"os2_supxsize",
14177      (getter)PyFF_Font_get_OS2_os2_supxsize, (setter)PyFF_Font_set_OS2_os2_supxsize,
14178      (char *)"OS/2 Superscript XSize", NULL},
14179     {(char *)"os2_supxoff",
14180      (getter)PyFF_Font_get_OS2_os2_supxoff, (setter)PyFF_Font_set_OS2_os2_supxoff,
14181      (char *)"OS/2 Superscript XOffset", NULL},
14182     {(char *)"os2_supysize",
14183      (getter)PyFF_Font_get_OS2_os2_supysize, (setter)PyFF_Font_set_OS2_os2_supysize,
14184      (char *)"OS/2 Superscript YSize", NULL},
14185     {(char *)"os2_supyoff",
14186      (getter)PyFF_Font_get_OS2_os2_supyoff, (setter)PyFF_Font_set_OS2_os2_supyoff,
14187      (char *)"OS/2 Superscript YOffset", NULL},
14188     {(char *)"os2_strikeysize",
14189      (getter)PyFF_Font_get_OS2_os2_strikeysize, (setter)PyFF_Font_set_OS2_os2_strikeysize,
14190      (char *)"OS/2 Strikethrough YSize", NULL},
14191     {(char *)"os2_strikeypos",
14192      (getter)PyFF_Font_get_OS2_os2_strikeypos, (setter)PyFF_Font_set_OS2_os2_strikeypos,
14193      (char *)"OS/2 Strikethrough YPosition", NULL},
14194     {(char *)"os2_capheight",
14195      (getter)PyFF_Font_get_OS2_os2_capheight, (setter)PyFF_Font_set_OS2_os2_capheight,
14196      (char *)"OS/2 Capital Height", NULL},
14197     {(char *)"os2_xheight",
14198      (getter)PyFF_Font_get_OS2_os2_xheight, (setter)PyFF_Font_set_OS2_os2_xheight,
14199      (char *)"OS/2 x Height", NULL},
14200     {(char *)"os2_family_class",
14201      (getter)PyFF_Font_get_OS2_os2_family_class, (setter)PyFF_Font_set_OS2_os2_family_class,
14202      (char *)"OS/2 Family Class", NULL},
14203     {(char *)"os2_use_typo_metrics",
14204      (getter)PyFF_Font_get_use_typo_metrics, (setter)PyFF_Font_set_use_typo_metrics,
14205      (char *)"OS/2 Flag MS thinks is necessary to encourage people to follow the standard and use typographic metrics", NULL},
14206     {(char *)"os2_weight_width_slope_only",
14207      (getter)PyFF_Font_get_weight_width_slope_only, (setter)PyFF_Font_set_weight_width_slope_only,
14208      (char *)"OS/2 Flag MS thinks is necessary", NULL},
14209     {(char *)"os2_codepages",
14210      (getter)PyFF_Font_get_os2codepages, (setter)PyFF_Font_set_os2codepages,
14211      (char *)"The 2 element OS/2 codepage tuple", NULL},
14212     {(char *)"os2_unicoderanges",
14213      (getter)PyFF_Font_get_os2unicoderanges, (setter)PyFF_Font_set_os2unicoderanges,
14214      (char *)"The 4 element OS/2 unicode ranges tuple", NULL},
14215     {(char *)"os2_panose",
14216      (getter)PyFF_Font_get_OS2_panose, (setter)PyFF_Font_set_OS2_panose,
14217      (char *)"The 10 element OS/2 Panose tuple", NULL},
14218     {(char *)"os2_vendor",
14219      (getter)PyFF_Font_get_OS2_vendor, (setter)PyFF_Font_set_OS2_vendor,
14220      (char *)"The 4 character OS/2 vendor string", NULL},
14221     {(char *)"changed",
14222      (getter)PyFF_Font_get_changed, (setter)PyFF_Font_set_changed,
14223      (char *)"Flag indicating whether the font has been changed since it was loaded (read only)", NULL},
14224     {(char *)"isnew",
14225      (getter)PyFF_Font_get_new, NULL,
14226      (char *)"Flag indicating whether the font is new (read only)", NULL},
14227     {(char *)"hasvmetrics",
14228      (getter)PyFF_Font_get_hasvmetrics, (setter)PyFF_Font_set_hasvmetrics,
14229      (char *)"Flag indicating whether the font contains vertical metrics", NULL},
14230     {(char *)"onlybitmaps",
14231      (getter)PyFF_Font_get_onlybitmaps, (setter)PyFF_Font_set_onlybitmaps,
14232      (char *)"Flag indicating whether the font contains bitmap strikes but no outlines", NULL},
14233     {(char *)"encoding",
14234      (getter)PyFF_Font_get_encoding, (setter)PyFF_Font_set_encoding,
14235      (char *)"The encoding used for indexing the font", NULL },
14236     {(char *)"is_quadratic",
14237      (getter)PyFF_Font_get_is_quadratic, (setter)PyFF_Font_set_is_quadratic,
14238      (char *)"Flag indicating whether the font contains quadratic splines (truetype) or cubic (postscript)", NULL},
14239     {(char *)"multilayer",
14240      (getter)PyFF_Font_get_multilayer, NULL,
14241      (char *)"Flag indicating whether the font is multilayered (type3) or not (readonly)", NULL},
14242     {(char *)"strokedfont",
14243      (getter)PyFF_Font_get_strokedfont, (setter)PyFF_Font_set_strokedfont,
14244      (char *)"Flag indicating whether the font is a stroked font or not", NULL},
14245     {(char *)"guide",
14246      (getter)PyFF_Font_get_guide, (setter)PyFF_Font_set_guide,
14247      (char *)"The Contours that make up the guide layer of the font", NULL},
14248     {(char *)"markClasses",
14249      (getter)PyFF_Font_get_mark_classes, (setter)PyFF_Font_set_mark_classes,
14250      (char *)"A tuple each entry of which is itself a tuple containing a mark-class-name and a tuple of glyph-names", NULL},
14251     PYGETSETDEF_EMPTY  /* Sentinel */
14252 };
14253 
14254 /* ************************************************************************** */
14255 /* Font Methods */
14256 /* ************************************************************************** */
14257 
PyFFFont_GetTableData(PyFF_Font * self,PyObject * args)14258 static PyObject *PyFFFont_GetTableData(PyFF_Font *self, PyObject *args) {
14259     char *table_name;
14260     uint32 tag;
14261     struct ttf_table *tab;
14262     PyObject *binstr;
14263 
14264     if ( CheckIfFontClosed(self) )
14265 return (NULL);
14266     if ( !PyArg_ParseTuple(args,"s",&table_name) )
14267 return( NULL );
14268     tag = StrToTag(table_name,NULL);
14269     if ( tag==BAD_TAG )
14270 return( NULL );
14271 
14272     for ( tab=self->fv->sf->ttf_tables; tab!=NULL && tab->tag!=tag; tab=tab->next );
14273     if ( tab==NULL )
14274 	for ( tab=self->fv->sf->ttf_tab_saved; tab!=NULL && tab->tag!=tag; tab=tab->next );
14275 
14276     if ( tab==NULL )
14277 Py_RETURN_NONE;
14278 
14279     binstr = PyBytes_FromStringAndSize((char *) tab->data,tab->len);
14280 return( binstr );
14281 }
14282 
TableAddInstrs(SplineFont * sf,uint32 tag,int replace,uint8 * instrs,int icnt)14283 static void TableAddInstrs(SplineFont *sf, uint32 tag,int replace,
14284 			   uint8 *instrs,int icnt) {
14285     struct ttf_table *tab;
14286 
14287     for ( tab=sf->ttf_tables; tab!=NULL && tab->tag!=tag; tab=tab->next );
14288     if ( tab==NULL )
14289 	for ( tab=sf->ttf_tab_saved; tab!=NULL && tab->tag!=tag; tab=tab->next );
14290 
14291     if ( replace && tab!=NULL ) {
14292 	free(tab->data);
14293 	tab->data = NULL;
14294 	tab->len = tab->maxlen = 0;
14295     }
14296     if ( icnt==0 )
14297 return;
14298     if ( tab==NULL ) {
14299 	tab = chunkalloc(sizeof( struct ttf_table ));
14300 	tab->tag = tag;
14301 	if ( tag==CHR('p','r','e','p') || tag==CHR('f','p','g','m') ||
14302 		tag==CHR('c','v','t',' ') || tag==CHR('m','a','x','p') ) {
14303 	    tab->next = sf->ttf_tables;
14304 	    sf->ttf_tables = tab;
14305 	} else {
14306 	    tab->next = sf->ttf_tab_saved;
14307 	    sf->ttf_tab_saved = tab;
14308 	}
14309     }
14310     if ( tab->data==NULL ) {
14311 	tab->data = malloc(icnt);
14312 	memcpy(tab->data,instrs,icnt);
14313 	tab->len = icnt;
14314     } else {
14315 	uint8 *newi = malloc(icnt+tab->len);
14316 	memcpy(newi,tab->data,tab->len);
14317 	memcpy(newi+tab->len,instrs,icnt);
14318 	free(tab->data);
14319 	tab->data = newi;
14320 	tab->len += icnt;
14321     }
14322     tab->maxlen = tab->len;
14323 }
14324 
PyFFFont_SetTableData(PyFF_Font * self,PyObject * args)14325 static PyObject *PyFFFont_SetTableData(PyFF_Font *self, PyObject *args) {
14326     char *table_name;
14327     uint32 tag;
14328     PyObject *tuple;
14329     uint8 *instrs;
14330     int icnt, i;
14331 
14332     if ( CheckIfFontClosed(self) )
14333 return (NULL);
14334     if ( !PyArg_ParseTuple(args,"sO",&table_name,&tuple) )
14335 return( NULL );
14336     tag = StrToTag(table_name,NULL);
14337     if ( tag==BAD_TAG )
14338 return( NULL );
14339 
14340     if ( tuple==Py_None ) {
14341 	SFRemoveSavedTable(self->fv->sf,tag);
14342 Py_RETURN(self);
14343     }
14344 
14345     if ( !PySequence_Check(tuple)) {
14346 	PyErr_Format(PyExc_TypeError, "Argument must be a tuple" );
14347 return( NULL );
14348     }
14349     if ( PyBytes_Check(tuple)) {
14350 	char *space; Py_ssize_t len;
14351 	PyBytes_AsStringAndSize(tuple,&space,&len);
14352 	instrs = calloc(len,sizeof(uint8));
14353 	icnt = len;
14354 	memcpy(instrs,space,len);
14355     } else {
14356 	icnt = PySequence_Size(tuple);
14357 	instrs = malloc(icnt);
14358 	for ( i=0; i<icnt; ++i ) {
14359 	    PyObject *val = PySequence_GetItem(tuple, i);
14360 	    instrs[i] = PyLong_AsLong(val);
14361 	    Py_XDECREF(val);
14362 	    if ( PyErr_Occurred()) {
14363 		free(instrs);
14364 return( NULL );
14365 	    }
14366 	}
14367     }
14368     TableAddInstrs(self->fv->sf,tag,true,instrs,icnt);
14369     free(instrs);
14370 Py_RETURN(self);
14371 }
14372 
PyFFFont_regenBitmaps(PyFF_Font * self,PyObject * args)14373 static PyObject *PyFFFont_regenBitmaps(PyFF_Font *self,PyObject *args) {
14374     if ( CheckIfFontClosed(self) )
14375 return (NULL);
14376     if ( bitmapper(self,args,false)==-1 )
14377 return( NULL );
14378 
14379 Py_RETURN(self);
14380 }
14381 
PyFFFont_importBitmaps(PyFF_Font * self,PyObject * args)14382 static PyObject *PyFFFont_importBitmaps(PyFF_Font *self,PyObject *args) {
14383     char *filename;
14384     char *locfilename = NULL, *ext;
14385     int to_background = -1, back=false;
14386     FontViewBase *fv;
14387     int ok, format;
14388 
14389     if ( CheckIfFontClosed(self) )
14390 return (NULL);
14391     fv = self->fv;
14392     if ( !PyArg_ParseTuple(args,"s|i",&filename,
14393 	    &to_background) )
14394 return( NULL );
14395     locfilename = utf82def_copy(filename);
14396 
14397     ext = strrchr(locfilename,'.');
14398     if ( ext==NULL ) {
14399 	int len = strlen(locfilename);
14400 	ext = locfilename+len-2;
14401 	if ( ext[0]!='p' || ext[1]!='k' ) {
14402 	    PyErr_Format(PyExc_EnvironmentError, "No extension for bitmap font");
14403 return( NULL );
14404 	}
14405     }
14406     if ( strmatch(ext,".bdf")==0 || strmatch(ext-4,".bdf.gz")==0 )
14407 	format = fv_bdf;
14408     else if ( strmatch(ext,".pcf")==0 || strmatch(ext-4,".pcf.gz")==0 )
14409 	format = fv_pcf;
14410     else if ( strmatch(ext,".ttf")==0 || strmatch(ext,".otf")==0 || strmatch(ext,".otb")==0 )
14411 	format = fv_ttf;
14412     else if ( strmatch(ext,"pk")==0 || strmatch(ext,".pk")==0 ) {
14413 	format = fv_pk;
14414 	back = true;
14415     } else {
14416 	PyErr_Format(PyExc_EnvironmentError, "Bad extension for bitmap font");
14417 return( NULL );
14418     }
14419     if ( to_background!=-1 )
14420 	back = to_background;
14421     if ( format==fv_bdf )
14422 	ok = FVImportBDF(fv,locfilename,false, back);
14423     else if ( format==fv_pcf )
14424 	ok = FVImportBDF(fv,locfilename,2, back);
14425     else if ( format==fv_ttf )
14426 	ok = FVImportMult(fv,locfilename, back, bf_ttf);
14427     else if ( format==fv_pk )
14428 	ok = FVImportBDF(fv,locfilename,true, back);
14429     free(locfilename);
14430     if ( !ok ) {
14431 	PyErr_Format(PyExc_EnvironmentError, "Could not load bitmap font");
14432 return( NULL );
14433     }
14434 
14435 Py_RETURN(self);
14436 }
14437 
14438 /* Font comparison flags */
14439 static struct flaglist compflags[] = {
14440     { "outlines",		  1 },
14441     { "outlines-exactly",	  2 },
14442     { "warn-outlines-mismatch",	  4 },
14443     { "hints",			  8 },
14444     { "warn-refs-unlink",	  0x40 },
14445     { "strikes",		  0x80 },
14446     { "fontnames",		  0x100 },
14447     { "gpos",			  0x200 },
14448     { "gsub",			  0x400 },
14449     { "add-outlines",		  0x800 },
14450     { "create-glyphs",		  0x1000 },
14451     FLAGLIST_EMPTY /* Sentinel */
14452 };
14453 
PyFFFont_compareFonts(PyFF_Font * self,PyObject * args)14454 static PyObject *PyFFFont_compareFonts(PyFF_Font *self,PyObject *args) {
14455     /* Compare the current font against the named one	     */
14456     /* output to a file (used /dev/null if no output wanted) */
14457     /* flags control what tests are done		     */
14458     PyFF_Font *other;
14459     PyObject *flagstuple, *ret;
14460     FILE *diffs;
14461     int flags;
14462     char *filename, *locfilename;
14463 
14464     if ( CheckIfFontClosed(self) )
14465 return (NULL);
14466     if ( !PyArg_ParseTuple(args,"OsO", &other, &filename, &flagstuple ))
14467 return( NULL );
14468     locfilename = utf82def_copy(filename);
14469 
14470     if ( !PyType_IsSubtype(&PyFF_FontType, Py_TYPE(other)) ) {
14471 	PyErr_Format(PyExc_TypeError,"First argument must be a fontforge font");
14472 	free(locfilename);
14473 return( NULL );
14474     }
14475     if ( CheckIfFontClosed(other) )
14476 return (NULL);
14477     flags = FlagsFromTuple(flagstuple,compflags,"comparison flag");
14478     if ( flags==FLAG_UNKNOWN ) {
14479 	free(locfilename);
14480 return( NULL );
14481     }
14482 
14483     if ( strcmp(locfilename,"-")==0 )
14484 	diffs = stdout;
14485     else {
14486 	diffs = fopen(locfilename,"w");
14487 	if ( diffs==NULL ) {
14488 	    PyErr_SetFromErrnoWithFilename(PyExc_IOError,locfilename);
14489 	    free(locfilename);
14490 return( NULL );
14491 	}
14492     }
14493 
14494     free( locfilename );
14495 
14496     ret = Py_BuildValue("i", CompareFonts(self->fv->sf, self->fv->map, other->fv->sf, diffs, flags ));
14497     if ( diffs!=stdout )
14498 	fclose( diffs );
14499 return( ret );
14500 }
14501 
PyFFFont_appendSFNTName(PyFF_Font * self,PyObject * args)14502 static PyObject *PyFFFont_appendSFNTName(PyFF_Font *self, PyObject *args) {
14503     SplineFont *sf;
14504     struct ttflangname dummy;
14505     int i;
14506 
14507     if ( CheckIfFontClosed(self) )
14508 return (NULL);
14509     sf = self->fv->sf;
14510     memset(&dummy,0,sizeof(dummy));
14511     DefaultTTFEnglishNames(&dummy, sf);
14512 
14513     if ( !SetSFNTName(sf,args,&dummy) )
14514 return( NULL );
14515 
14516     for ( i=0; i<ttf_namemax; ++i )
14517 	free( dummy.names[i]);
14518 
14519 Py_RETURN( self );
14520 }
14521 
PyFFFont_cidConvertTo(PyFF_Font * self,PyObject * args)14522 static PyObject *PyFFFont_cidConvertTo(PyFF_Font *self,PyObject *args) {
14523     FontViewBase *fv;
14524     SplineFont *sf;
14525     struct cidmap *map;
14526     char *registry, *ordering;
14527     int supplement;
14528 
14529     if ( CheckIfFontClosed(self) )
14530 return (NULL);
14531     fv = self->fv;
14532     sf = fv->sf;
14533     if ( sf->cidmaster!=NULL ) {
14534 	PyErr_Format(PyExc_EnvironmentError,"This font is already a CID keyed font." );
14535 return( NULL );
14536     }
14537     if ( !PyArg_ParseTuple(args,"ssi", &registry, &ordering, &supplement ))
14538 return( NULL );
14539     map = FindCidMap( registry, ordering, supplement, sf);
14540     if ( map == NULL ) {
14541 	PyErr_Format(PyExc_EnvironmentError,"No cidmap matching given ROS (%s-%s-%d)",
14542 		registry, ordering, supplement );
14543 return( NULL );
14544     }
14545     MakeCIDMaster(sf, fv->map, false, NULL, map);
14546 Py_RETURN( self );
14547 }
14548 
PyFFFont_cidConvertByCmap(PyFF_Font * self,PyObject * args)14549 static PyObject *PyFFFont_cidConvertByCmap(PyFF_Font *self,PyObject *args) {
14550     FontViewBase *fv;
14551     SplineFont *sf;
14552     char *locfilename;
14553 
14554     if ( CheckIfFontClosed(self) )
14555 return (NULL);
14556     fv = self->fv;
14557     sf = fv->sf;
14558     if ( sf->cidmaster!=NULL ) {
14559 	PyErr_Format(PyExc_EnvironmentError,"This font is already a CID keyed font." );
14560 return( NULL );
14561     }
14562     if ( !PyArg_ParseTuple(args,"s", &locfilename ))
14563 return( NULL );
14564     MakeCIDMaster(sf, fv->map, true, locfilename, NULL);
14565 Py_RETURN( self );
14566 }
14567 
PyFFFont_cidFlatten(PyFF_Font * self,PyObject * UNUSED (args))14568 static PyObject *PyFFFont_cidFlatten(PyFF_Font *self, PyObject *UNUSED(args)) {
14569     SplineFont *cidmaster;
14570 
14571     if ( CheckIfFontClosed(self) )
14572 return (NULL);
14573     cidmaster = self->fv->cidmaster;
14574     if ( cidmaster==NULL ) {
14575 	PyErr_Format(PyExc_EnvironmentError,"This font is not a CID keyed font." );
14576 return( NULL );
14577     }
14578 
14579     SFFlatten(&cidmaster);
14580 Py_RETURN( self );
14581 }
14582 
PyFFFont_cidFlattenByCMap(PyFF_Font * self,PyObject * args)14583 static PyObject *PyFFFont_cidFlattenByCMap(PyFF_Font *self,PyObject *args) {
14584     SplineFont *cidmaster;
14585     char *locfilename;
14586 
14587     if ( CheckIfFontClosed(self) )
14588 return (NULL);
14589     cidmaster = self->fv->cidmaster;
14590     if ( cidmaster==NULL ) {
14591 	PyErr_Format(PyExc_EnvironmentError,"This font is not a CID keyed font." );
14592 return( NULL );
14593     }
14594 
14595     if ( !PyArg_ParseTuple(args,"s", &locfilename ))
14596 return( NULL );
14597 
14598     if ( !SFFlattenByCMap(&cidmaster,locfilename)) {
14599 	PyErr_Format(PyExc_EnvironmentError,"Can't find (or can't parse) cmap file: %s", locfilename);
14600 return( NULL );
14601     }
14602 Py_RETURN( self );
14603 }
14604 
PyFFFont_cidInsertBlankSubFont(PyFF_Font * self,PyObject * UNUSED (args))14605 static PyObject *PyFFFont_cidInsertBlankSubFont(PyFF_Font *self, PyObject *UNUSED(args)) {
14606     FontViewBase *fv;
14607     SplineFont *cidmaster, *sf;
14608     struct cidmap *map;
14609 
14610     if ( CheckIfFontClosed(self) )
14611 return (NULL);
14612     fv = self->fv;
14613     cidmaster = fv->cidmaster;
14614     if ( cidmaster==NULL ) {
14615 	PyErr_Format(PyExc_EnvironmentError,"This font is not a CID keyed font." );
14616 return( NULL );
14617     }
14618     if ( cidmaster->subfontcnt>=255 ) {
14619 	PyErr_Format(PyExc_EnvironmentError,"You may have at most 255 subfonts in a CID keyed font." );
14620 return( NULL );
14621     }
14622 
14623     map = FindCidMap(cidmaster->cidregistry,cidmaster->ordering,cidmaster->supplement,cidmaster);
14624     sf = SplineFontBlank(MaxCID(map));
14625     sf->glyphcnt = sf->glyphmax;
14626     sf->cidmaster = cidmaster;
14627     sf->display_antialias = fv->sf->display_antialias;
14628     sf->display_bbsized = fv->sf->display_bbsized;
14629     sf->display_size = fv->sf->display_size;
14630     sf->private = calloc(1,sizeof(struct psdict));
14631     PSDictChangeEntry(sf->private,"lenIV","1");		/* It's 4 by default, in CIDs the convention seems to be 1 */
14632     FVInsertInCID(fv,sf);
14633 Py_RETURN( self );
14634 }
14635 
PyFFFont_cidRemoveSubFont(PyFF_Font * self,PyObject * UNUSED (args))14636 static PyObject *PyFFFont_cidRemoveSubFont(PyFF_Font *self, PyObject *UNUSED(args)) {
14637     FontViewBase *fv, *fvs;
14638     SplineFont *cidmaster, *sf, *replace;
14639     int i;
14640 
14641     if ( CheckIfFontClosed(self) )
14642 return (NULL);
14643     fv = self->fv;
14644     sf = fv->sf;
14645     cidmaster = fv->cidmaster;
14646     if ( cidmaster==NULL ) {
14647 	PyErr_Format(PyExc_EnvironmentError,"This font is not a CID keyed font." );
14648 return( NULL );
14649     }
14650     if ( cidmaster->subfontcnt<=1 ) {
14651 	PyErr_Format(PyExc_EnvironmentError,"You must have at least 1 subfont in a CID keyed font." );
14652 return( NULL );
14653     }
14654 
14655     for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
14656 	SCCloseAllViews(sf->glyphs[i]);
14657     }
14658     MVDestroyAll(sf);
14659 
14660     for ( i=0; i<cidmaster->subfontcnt; ++i )
14661 	if ( cidmaster->subfonts[i]==sf )
14662     break;
14663     replace = i==0?cidmaster->subfonts[1]:cidmaster->subfonts[i-1];
14664     while ( i<cidmaster->subfontcnt-1 ) {
14665 	cidmaster->subfonts[i] = cidmaster->subfonts[i+1];
14666 	++i;
14667     }
14668     --cidmaster->subfontcnt;
14669 
14670     for ( fvs=sf->fv; fvs!=NULL; fvs=fvs->nextsame ) {
14671 	if ( fvs->sf==sf )
14672 	    CIDSetEncMap( fvs,replace);
14673     }
14674     FontViewReformatAll(sf);
14675     SplineFontFree(sf);
14676 Py_RETURN( self );
14677 }
14678 
addLookupSubtable(SplineFont * sf,char * lookup,char * new_subtable,char * after_str)14679 static struct lookup_subtable *addLookupSubtable(SplineFont *sf, char *lookup,
14680 						 char *new_subtable, char *after_str) {
14681     OTLookup *otl;
14682     struct lookup_subtable *sub, *after=NULL;
14683     int is_v;
14684 
14685     otl = SFFindLookup(sf,lookup);
14686     if ( otl==NULL ) {
14687 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", lookup );
14688 return( NULL );
14689     }
14690     if ( after_str!=NULL ) {
14691 	after = SFFindLookupSubtable(sf,after_str);
14692 	if ( after==NULL ) {
14693 	    PyErr_Format(PyExc_EnvironmentError, "No lookup subtable named %s", after_str );
14694 return( NULL );
14695 	} else if ( after->lookup!=otl ) {
14696 	    PyErr_Format(PyExc_EnvironmentError, "Subtable, %s, is not in lookup %s.", after_str, lookup );
14697 return( NULL );
14698 	}
14699     }
14700 
14701     if ( sf->cidmaster ) sf = sf->cidmaster;
14702 
14703     if ( SFFindLookupSubtable(sf,new_subtable)!=NULL ) {
14704 	PyErr_Format(PyExc_EnvironmentError, "A lookup subtable named %s already exists", new_subtable);
14705 return( NULL );
14706     }
14707 
14708     sub = chunkalloc(sizeof(struct lookup_subtable));
14709     sub->lookup = otl;
14710     sub->subtable_name = copy(new_subtable);
14711     if ( after!=NULL ) {
14712 	sub->next = after->next;
14713 	after->next = sub;
14714     } else {
14715 	sub->next = otl->subtables;
14716 	otl->subtables = sub;
14717     }
14718 
14719     switch ( otl->lookup_type ) {
14720       case gpos_cursive:
14721       case gpos_mark2base:
14722       case gpos_mark2ligature:
14723       case gpos_mark2mark:
14724         sub->anchor_classes = true;
14725       break;
14726       case gpos_pair:
14727         is_v = VerticalKernFeature(sf, otl, false);
14728         if ( is_v==-1 ) is_v = false;
14729         sub->vertical_kerning = is_v;
14730         sub->per_glyph_pst_or_kern = true;
14731       break;
14732       case gpos_single:
14733       case gsub_single:
14734       case gsub_multiple:
14735       case gsub_alternate:
14736       case gsub_ligature:
14737         sub->per_glyph_pst_or_kern = true;
14738       break;
14739       default:
14740       break;
14741     }
14742 
14743 return( sub );
14744 }
14745 
PyFFFont_buildOrReplaceAALTFeatures(PyFF_Font * self,PyObject * UNUSED (args))14746 static PyObject *PyFFFont_buildOrReplaceAALTFeatures(PyFF_Font *self, PyObject *UNUSED(args)) {
14747     SplineFont *sf;
14748 
14749     if ( CheckIfFontClosed(self) )
14750 return (NULL);
14751     sf = self->fv->sf;
14752     AddNewAALTFeatures(sf);
14753 
14754 Py_RETURN( self );
14755 }
14756 
PyFFFont_addAnchorClass(PyFF_Font * self,PyObject * args)14757 static PyObject *PyFFFont_addAnchorClass(PyFF_Font *self, PyObject *args) {
14758     SplineFont *sf;
14759     char *subtable, *anchor_name, *typename;
14760     struct lookup_subtable *sub;
14761     AnchorClass *ac;
14762     int ac_type, aptype;
14763 
14764     if ( CheckIfFontClosed(self) )
14765 return (NULL);
14766     sf = self->fv->sf;
14767     if ( !PyArg_ParseTuple(args,"ss|s", &subtable, &anchor_name, &typename ))
14768 return( NULL );
14769 
14770     sub = SFFindLookupSubtable(sf,subtable);
14771     if ( sub==NULL && !typename ) {
14772 	PyErr_Format(PyExc_EnvironmentError, "No subtable named %s", subtable );
14773 return( NULL );
14774     }
14775     else if ( sub==NULL) {
14776         aptype = FlagsFromString(typename,ap_types,"anchor type");
14777         ac_type = aptype==at_basechar                           ? act_mark :
14778                         aptype==at_baselig                      ? act_mklg :
14779                         aptype==at_cexit || aptype==at_centry   ? act_curs :
14780                                                                   act_mkmk ;
14781     }
14782     else
14783         ac_type = sub->lookup->lookup_type==gpos_cursive                ? act_curs :
14784                         sub->lookup->lookup_type==gpos_mark2base        ? act_mark :
14785                         sub->lookup->lookup_type==gpos_mark2ligature    ? act_mklg :
14786                         sub->lookup->lookup_type==gpos_mark2mark        ? act_mkmk :
14787                                                                           act_unknown;
14788     if ( ac_type == act_unknown ) {
14789 	PyErr_Format(PyExc_EnvironmentError, "Cannot add an anchor class to %s, it has the wrong lookup type", subtable );
14790 return( NULL );
14791     }
14792     for ( ac=sf->anchor; ac!=NULL; ac=ac->next ) {
14793 	if ( strcmp(ac->name,anchor_name)==0 )
14794     break;
14795     }
14796     if ( ac!=NULL && ac->subtable!=NULL ) {
14797 	PyErr_Format(PyExc_EnvironmentError, "An anchor class named %s already exists", anchor_name );
14798 return( NULL );
14799     }
14800     else if ( ac!=NULL ) {  /* we are associating an existing implicit anchor class */
14801         ac->subtable = sub;
14802         ac->type = ac_type;
14803     }
14804     else {
14805         ac = chunkalloc(sizeof(AnchorClass));
14806         ac->name = copy( anchor_name );
14807         ac->subtable = sub;
14808         ac->type = ac_type;
14809         ac->next = sf->anchor;
14810         sf->anchor = ac;
14811     }
14812 
14813 Py_RETURN( self );
14814 }
14815 
PyFFFont_removeAnchorClass(PyFF_Font * self,PyObject * args)14816 static PyObject *PyFFFont_removeAnchorClass(PyFF_Font *self, PyObject *args) {
14817     SplineFont *sf;
14818     char *anchor_name;
14819     AnchorClass *ac;
14820 
14821     if ( CheckIfFontClosed(self) )
14822 return (NULL);
14823     sf = self->fv->sf;
14824     if ( !PyArg_ParseTuple(args,"s", &anchor_name ))
14825 return( NULL );
14826 
14827     for ( ac=sf->anchor; ac!=NULL; ac=ac->next ) {
14828 	if ( strcmp(ac->name,anchor_name)==0 )
14829     break;
14830     }
14831     if ( ac==NULL ) {
14832 	PyErr_Format(PyExc_EnvironmentError, "No anchor class named %s exists", anchor_name );
14833 return( NULL );
14834     }
14835     SFRemoveAnchorClass(sf,ac);
14836 Py_RETURN( self );
14837 }
14838 
ParseClassNames(PyObject * classes,char *** class_strs)14839 static int ParseClassNames(PyObject *classes,char ***class_strs) {
14840     int cnt, i;
14841     char **cls;
14842 
14843     *class_strs = NULL;
14844     cnt = PySequence_Size(classes);
14845     if ( cnt==-1 )
14846 return( -1 );
14847     *class_strs = cls = malloc(cnt*sizeof(char *));
14848     for ( i=0; i<cnt; ++i ) {
14849 	PyObject *thingy = PySequence_GetItem(classes,i);
14850 	if ( i==0 && thingy==Py_None ) {
14851 	    cls[i] = NULL;
14852 	    Py_DECREF(thingy);
14853 	} else {
14854 	    cls[i] = GlyphNamesFromTuple(thingy);
14855 	    Py_XDECREF(thingy);
14856 	    if ( cls[i]==NULL )
14857 return( -1 );
14858 	}
14859     }
14860 return( cnt );
14861 }
14862 
MakeClassNameTuple(int cnt,char ** classes)14863 static PyObject *MakeClassNameTuple(int cnt, char**classes) {
14864     PyObject *tuple;
14865     int i;
14866 
14867     tuple = PyTuple_New(cnt);
14868     for ( i=0; i<cnt; ++i ) {
14869 	if ( classes[i]==NULL ) {
14870 	    PyTuple_SetItem(tuple,i,Py_None);
14871 	    Py_INCREF(Py_None);
14872 	} else
14873 	    PyTuple_SetItem(tuple,i,TupleOfGlyphNames(classes[i],0));
14874     }
14875 return( tuple );
14876 }
14877 
GlyphsFromSelection(FontViewBase * fv)14878 static SplineChar **GlyphsFromSelection(FontViewBase *fv) {
14879     SplineFont *sf;
14880     EncMap *map;
14881     int selcnt;
14882     int enc,gid;
14883     SplineChar **glyphlist, *sc;
14884 
14885     map = fv->map;
14886     sf = fv->sf;
14887     selcnt=0;
14888     for ( enc=0; enc<map->enccount; ++enc ) {
14889 	if ( fv->selected[enc] && (gid=map->map[enc])!=-1 &&
14890 		SCWorthOutputting(sf->glyphs[gid]))
14891 	    ++selcnt;
14892     }
14893     if ( selcnt<=1 ) {
14894 	PyErr_Format(PyExc_EnvironmentError, "Please select some glyphs in the font view for FontForge to put into classes.");
14895 return(NULL);
14896     }
14897 
14898     glyphlist = malloc((selcnt+1)*sizeof(SplineChar *));
14899     selcnt=0;
14900     for ( enc=0; enc<map->enccount; ++enc ) {
14901 	if ( fv->selected[enc] && (gid=map->map[enc])!=-1 &&
14902 		SCWorthOutputting(sc = sf->glyphs[gid]))
14903 	    glyphlist[selcnt++] = sc;
14904     }
14905     glyphlist[selcnt] = NULL;
14906 return( glyphlist );
14907 }
14908 
pyBuildClasses(FontViewBase * fv,struct lookup_subtable * sub,real good_enough,PyObject * list1,PyObject * list2)14909 static int pyBuildClasses(FontViewBase *fv,
14910 	struct lookup_subtable *sub,real good_enough,
14911 	PyObject *list1, PyObject *list2) {
14912     SplineChar **glyphlist, **first, **second;
14913 
14914     if ( list1==NULL ) {
14915 	glyphlist = GlyphsFromSelection(fv);
14916 	if ( glyphlist==NULL )
14917 return( false );
14918 	first = second = glyphlist;
14919     } else {
14920 	first = GlyphsFromTuple(fv->sf,list1);
14921 	second = GlyphsFromTuple(fv->sf,list2);
14922     }
14923     if ( first==NULL || second==NULL ) {
14924 	free(second); free(first);
14925 return( false );
14926     }
14927     AutoKern2BuildClasses(fv->sf,fv->active_layer,first,second,sub,
14928 	    sub->separation,0,sub->kerning_by_touch, sub->onlyCloser,
14929 	    !sub->dontautokern,
14930 	    good_enough);
14931     free(first);
14932     if ( first!=second )
14933 	free(second);
14934 return( true );
14935 }
14936 
pyAddOffsetAsIs(void * data,int left_index,int right_index,int kern)14937 static void pyAddOffsetAsIs(void *data,int left_index,int right_index, int kern) {
14938     struct lookup_subtable *sub = data;
14939     KernClass *kc = sub->kc;
14940 
14941     if ( !(sub->lookup->lookup_flags & pst_r2l) ) {
14942 	kc->offsets[left_index*kc->second_cnt+right_index] = kern;
14943     } else {
14944 	kc->offsets[right_index*kc->second_cnt+left_index] = kern;
14945     }
14946 }
14947 
pyAutoKernAll(FontViewBase * fv,struct lookup_subtable * sub)14948 static void pyAutoKernAll(FontViewBase *fv,struct lookup_subtable *sub ) {
14949     char **lefts, **rights;
14950     int lcnt, rcnt;
14951     KernClass *kc = sub->kc;
14952 
14953     if ( !(sub->lookup->lookup_flags & pst_r2l) ) {
14954 	lefts = kc->firsts; lcnt = kc->first_cnt;
14955 	rights = kc->seconds; rcnt = kc->second_cnt;
14956     } else {
14957 	lefts = kc->seconds; lcnt = kc->second_cnt;
14958 	rights = kc->firsts; rcnt=kc->first_cnt;
14959     }
14960     AutoKern2NewClass(fv->sf,fv->active_layer, lefts, rights, lcnt, rcnt,
14961 	    pyAddOffsetAsIs, sub, sub->separation, 0, sub->kerning_by_touch,
14962 	    sub->onlyCloser, 0);
14963 }
14964 
PyFFFont_addKerningClass(PyFF_Font * self,PyObject * args)14965 static PyObject *PyFFFont_addKerningClass(PyFF_Font *self, PyObject *args) {
14966     FontViewBase *fv;
14967     SplineFont *sf;
14968 
14969     if ( CheckIfFontClosed(self) )
14970 return (NULL);
14971 
14972     fv = self->fv;
14973     sf = fv->sf;
14974     char *lookup, *subtable, *after_str=NULL;
14975     int i;
14976     struct lookup_subtable *sub;
14977     PyObject *class1s=NULL, *class2s=NULL, *offsets=NULL, *list1=NULL, *list2=NULL;
14978     PyObject *arg3, *arg4, *arg5;
14979     char **class1_strs, **class2_strs;
14980     int cnt1, cnt2, acnt;
14981     int16 *offs=NULL;
14982     int separation= -1, touch=0, do_autokern=false, only_closer=0, autokern=true;
14983     double class_error_distance = -1;
14984     /* arguments:
14985      *  (char *lookupname, char *newsubtabname, char ***classes1, char ***classes2, int *offsets [,char *after_sub_name])
14986      *  (char *lookupname, char *newsubtabname, int separation, char ***classes1, char ***classes2 [, int only_closer, int autokern, char *after_sub_name])
14987      *  (char *lookupname, char *newsubtabname, int separation, double err, char **list1, char **list2 [, int only_closer, int autokern, char *after_sub_name])
14988      *  (char *lookupname, char *newsubtabname, int separation, double err [, int only_closer, int autokern, char *after_sub_name])
14989      *  Also support arguments where [,int autokern] is absent as we used not to
14990      *  allow the user to specify it
14991      * First is fully specified set of classes with offsets cnt=5/6
14992      * Second fully specified set of classes, to be autokerned cnt=5/7
14993      * Third two lists of glyphs to be turned into classes and then autokerned cnt=6/8
14994      * Fourth turns the selection into a list of glyphs, to be used both left and right for two sets of classes to be autokerned cnt=4/6
14995      */
14996     if ( CheckIfFontClosed(self) )
14997 return (NULL);
14998     fv = self->fv;
14999     if ( (acnt = PyTuple_Size(args))<4 ) {
15000 	PyErr_Format(PyExc_EnvironmentError, "Too few arguments.");
15001 return( NULL );
15002     }
15003     arg3 = PyTuple_GetItem(args,2);
15004     arg4 = PyTuple_GetItem(args,3);
15005     do_autokern = true;
15006     if ( !PyLong_Check(arg3)) {
15007 	if ( !PyArg_ParseTuple(args,"ssOOO|s", &lookup, &subtable, &class1s, &class2s,
15008 		&offsets, &after_str ))
15009 return( NULL );
15010 	do_autokern = false;
15011     } else if ( !PyLong_Check(arg4) && !PyFloat_Check(arg4)) {
15012 	PyObject *arg7 = acnt>=7 ? PyTuple_GetItem(args,6) : NULL;
15013 	if ( arg7!=NULL && (PyLong_Check(arg7))) {
15014 	    if ( !PyArg_ParseTuple(args,"ssiOO|iis", &lookup, &subtable,
15015 		    &separation, &class1s, &class2s,
15016 		    &only_closer, &autokern, &after_str ))
15017 return( NULL );
15018 	} else {
15019 	    if ( !PyArg_ParseTuple(args,"ssiOO|is", &lookup, &subtable,
15020 		    &separation, &class1s, &class2s,
15021 		    &only_closer, &after_str ))
15022 return( NULL );
15023 	}
15024     } else if ( acnt>5 &&
15025 	    (arg5=PyTuple_GetItem(args,4)) && PyTuple_Check(arg5) ) {
15026 	PyObject *arg8 = acnt>=8 ? PyTuple_GetItem(args,7) : NULL;
15027 	if ( arg8!=NULL && (PyLong_Check(arg8))) {
15028 	    if ( !PyArg_ParseTuple(args,"ssidOO|iis", &lookup, &subtable,
15029 		    &separation, &class_error_distance, &list1, &list2,
15030 		    &only_closer, &autokern, &after_str ))
15031 return( NULL );
15032 	} else {
15033 	    if ( !PyArg_ParseTuple(args,"ssidOO|is", &lookup, &subtable,
15034 		    &separation, &class_error_distance, &list1, &list2,
15035 		    &only_closer, &after_str ))
15036 return( NULL );
15037 	}
15038     } else {
15039 	PyObject *arg6 = acnt>=6 ? PyTuple_GetItem(args,5) : NULL;
15040 	if ( arg6!=NULL && (PyLong_Check(arg6))) {
15041 	    if ( !PyArg_ParseTuple(args,"ssid|iis", &lookup, &subtable,
15042 		    &separation, &class_error_distance,
15043 		    &only_closer, &autokern, &after_str ))
15044 return( NULL );
15045 	} else {
15046 	    if ( !PyArg_ParseTuple(args,"ssid|is", &lookup, &subtable,
15047 		    &separation, &class_error_distance,
15048 		    &only_closer, &after_str ))
15049 return( NULL );
15050 	}
15051     }
15052     if ( separation==0 )
15053 	touch=1;
15054 
15055     if ( class1s!=NULL ) {
15056 	cnt1 = ParseClassNames(class1s,&class1_strs);
15057 	cnt2 = ParseClassNames(class2s,&class2_strs);
15058 	if ( offsets!=NULL ) {
15059 	    if ( cnt1*cnt2 != PySequence_Size(offsets) ) {
15060 		PyErr_Format(PyExc_ValueError, "There aren't enough kerning offsets for the number of kerning classes. Should be %d", cnt1*cnt2 );
15061 return( NULL );
15062 	    }
15063 	    offs = malloc(cnt1*cnt2*sizeof(int16));
15064 	    for ( i=0 ; i<cnt1*cnt2; ++i ) {
15065 		offs[i] = PyLong_AsLong(PySequence_GetItem(offsets,i));
15066 		if ( PyErr_Occurred()) {
15067                     free(offs);
15068 return( NULL );
15069 }
15070 	    }
15071 	} else
15072 	    offs = calloc(cnt1*cnt2,sizeof(int16));
15073     }
15074 
15075     sub = addLookupSubtable(sf, lookup, subtable, after_str);
15076     if ( sub==NULL ) {
15077         free(offs);
15078 return( NULL );
15079     }
15080     if ( sub->lookup->lookup_type!=gpos_pair ) {
15081 	PyErr_Format(PyExc_EnvironmentError, "Cannot add kerning data to %s, it has the wrong lookup type", lookup );
15082 	free(offs);
15083 return( NULL );
15084     }
15085     sub->per_glyph_pst_or_kern = false;
15086     if ( do_autokern ) {
15087 	sub->separation = separation;
15088 	sub->kerning_by_touch = touch;
15089 	sub->onlyCloser = only_closer;
15090 	sub->dontautokern = !autokern;
15091     }
15092     sub->kc = chunkalloc(sizeof(KernClass));
15093     sub->kc->subtable = sub;
15094     if ( class1s!=NULL ) {
15095 	sub->kc->first_cnt = cnt1;
15096 	sub->kc->second_cnt = cnt2;
15097 	sub->kc->firsts = class1_strs;
15098 	sub->kc->seconds = class2_strs;
15099 	sub->kc->offsets = offs;
15100 	sub->kc->adjusts = calloc(cnt1*cnt2,sizeof(DeviceTable));
15101 	if ( offsets==NULL )
15102 	    pyAutoKernAll(fv,sub);
15103     } else {
15104 	if ( !pyBuildClasses(fv,sub,class_error_distance,list1,list2) ) {
15105             free(offs);
15106 return( NULL );
15107         }
15108     }
15109 
15110     if ( sub->vertical_kerning ) {
15111 	sub->kc->next = sf->vkerns;
15112 	sf->vkerns = sub->kc;
15113     } else {
15114 	sub->kc->next = sf->kerns;
15115 	sf->kerns = sub->kc;
15116     }
15117 
15118 Py_RETURN( self );
15119 }
15120 
PyFFFont_alterKerningClass(PyFF_Font * self,PyObject * args)15121 static PyObject *PyFFFont_alterKerningClass(PyFF_Font *self, PyObject *args) {
15122     SplineFont *sf;
15123     char *subtable;
15124     int i;
15125     struct lookup_subtable *sub;
15126     PyObject *class1s, *class2s, *offsets;
15127     char **class1_strs, **class2_strs;
15128     int cnt1, cnt2;
15129     int16 *offs;
15130 
15131     if ( CheckIfFontClosed(self) )
15132 return (NULL);
15133     sf = self->fv->sf;
15134     if ( !PyArg_ParseTuple(args,"sOOO", &subtable, &class1s, &class2s,
15135 	    &offsets ))
15136 return( NULL );
15137 
15138     sub = SFFindLookupSubtable(sf,subtable);
15139     if ( sub==NULL ) {
15140 	PyErr_Format(PyExc_EnvironmentError, "No subtable named %s", subtable );
15141 return( NULL );
15142     }
15143     if ( sub->kc==NULL ) {
15144 	PyErr_Format(PyExc_EnvironmentError, "This subtable, %s, does not contain not a kerning class", subtable );
15145 return( NULL );
15146     }
15147 
15148     cnt1 = ParseClassNames(class1s,&class1_strs);
15149     cnt2 = ParseClassNames(class2s,&class2_strs);
15150     if ( cnt1*cnt2 != PySequence_Size(offsets) ) {
15151 	PyErr_Format(PyExc_ValueError, "There aren't enough kerning offsets for the number of kerning classes. Should be %d", cnt1*cnt2 );
15152 return( NULL );
15153     }
15154     offs = malloc(cnt1*cnt2*sizeof(int16));
15155     for ( i=0 ; i<cnt1*cnt2; ++i ) {
15156 	offs[i] = PyLong_AsLong(PySequence_GetItem(offsets,i));
15157 	if ( PyErr_Occurred()) {
15158 	    free(offs); free(class2_strs); free(class1_strs);
15159 return( NULL );
15160 	}
15161     }
15162 
15163     KernClassFreeContents(sub->kc);
15164     sub->kc->first_cnt = cnt1;
15165     sub->kc->second_cnt = cnt2;
15166     sub->kc->firsts = class1_strs;
15167     sub->kc->seconds = class2_strs;
15168     sub->kc->offsets = offs;
15169     sub->kc->adjusts = calloc(cnt1*cnt2,sizeof(DeviceTable));
15170 
15171 Py_RETURN( self );
15172 }
15173 
PyFFFont_getKerningClass(PyFF_Font * self,PyObject * args)15174 static PyObject *PyFFFont_getKerningClass(PyFF_Font *self, PyObject *args) {
15175     SplineFont *sf;
15176     char *subtable;
15177     struct lookup_subtable *sub;
15178     PyObject *offsets;
15179     int i;
15180 
15181     if ( CheckIfFontClosed(self) )
15182 return (NULL);
15183     sf = self->fv->sf;
15184     if ( !PyArg_ParseTuple(args,"s", &subtable ))
15185 return( NULL );
15186 
15187     sub = SFFindLookupSubtable(sf,subtable);
15188     if ( sub==NULL ) {
15189 	PyErr_Format(PyExc_EnvironmentError, "No subtable named %s", subtable );
15190 return( NULL );
15191     }
15192     if ( sub->kc==NULL ) {
15193 	PyErr_Format(PyExc_EnvironmentError, "This subtable, %s, does not contain not a kerning class", subtable );
15194 return( NULL );
15195     }
15196     offsets = PyTuple_New(sub->kc->first_cnt*sub->kc->second_cnt);
15197     for ( i=0; i<sub->kc->first_cnt*sub->kc->second_cnt; ++i )
15198 	PyTuple_SetItem(offsets,i,PyLong_FromLong(sub->kc->offsets[i]));
15199 
15200 return( Py_BuildValue("(OOO)",
15201 	MakeClassNameTuple(sub->kc->first_cnt,sub->kc->firsts),
15202 	MakeClassNameTuple(sub->kc->second_cnt,sub->kc->seconds),
15203 	offsets));
15204 }
15205 
PyFFFont_isKerningClass(PyFF_Font * self,PyObject * args)15206 static PyObject *PyFFFont_isKerningClass(PyFF_Font *self, PyObject *args) {
15207     SplineFont *sf;
15208     char *subtable;
15209     struct lookup_subtable *sub;
15210     PyObject *ret;
15211 
15212     if ( CheckIfFontClosed(self) )
15213 return (NULL);
15214     sf = self->fv->sf;
15215     if ( !PyArg_ParseTuple(args,"s", &subtable ))
15216 return( NULL );
15217 
15218     sub = SFFindLookupSubtable(sf,subtable);
15219     if ( sub==NULL || sub->kc==NULL )
15220 	ret = Py_False;
15221     else
15222 	ret = Py_True;
15223     Py_INCREF(ret);
15224 return( ret );
15225 }
15226 
PyFFFont_isVerticalKerning(PyFF_Font * self,PyObject * args)15227 static PyObject *PyFFFont_isVerticalKerning(PyFF_Font *self, PyObject *args) {
15228     SplineFont *sf;
15229     char *subtable;
15230     struct lookup_subtable *sub;
15231     PyObject *ret;
15232 
15233     if ( CheckIfFontClosed(self) )
15234 return (NULL);
15235     sf = self->fv->sf;
15236     if ( !PyArg_ParseTuple(args,"s", &subtable ))
15237 return( NULL );
15238 
15239     sub = SFFindLookupSubtable(sf,subtable);
15240     if ( sub==NULL || !sub->vertical_kerning )
15241 	ret = Py_False;
15242     else
15243 	ret = Py_True;
15244     Py_INCREF(ret);
15245 return( ret );
15246 }
15247 
15248 static const char *ak_keywords1[] = { "subTableName", "separation", "minKern",
15249 	"touch", "onlyCloser", "height", NULL };
15250 static const char *ak_keywords2[] = { "subTableName", "separation", "list1", "list2",
15251 	"minKern", "touch", "onlyCloser", "height", NULL };
15252 
PyFFFont_autoKern(PyFF_Font * self,PyObject * args,PyObject * keywds)15253 static PyObject *PyFFFont_autoKern(PyFF_Font *self, PyObject *args, PyObject *keywds) {
15254     FontViewBase *fv;
15255     SplineFont *sf;
15256     char *subtablename;
15257     int separation;
15258     PyObject *list1=NULL, *list2=NULL;
15259     SplineChar **first, **second, **left, **right;
15260     struct lookup_subtable *sub;
15261     int minkern = 10, touch=0, height=0, onlyCloser=0;
15262 
15263     if ( CheckIfFontClosed(self) )
15264 return (NULL);
15265     fv = self->fv;
15266     sf = fv->sf;
15267     if ( PyTuple_Size(args)==2 ) {
15268 	if ( !PyArg_ParseTupleAndKeywords(args, keywds, "si|iiii", (char **)ak_keywords1,
15269 		&subtablename, &separation, &minkern, &touch, &onlyCloser, &height))
15270 return( NULL );
15271     } else {
15272 	if ( !PyArg_ParseTupleAndKeywords(args, keywds, "siOO|iiii", (char **)ak_keywords2,
15273 		&subtablename, &separation, &list1, &list2, &minkern, &touch,
15274 		&onlyCloser, &height ))
15275 return( NULL );
15276     }
15277     sub = SFFindLookupSubtable(sf,subtablename);
15278     if ( sub==NULL ) {
15279 	PyErr_Format(PyExc_EnvironmentError, "No subtable named %s exists", subtablename );
15280 return( NULL );
15281     }
15282     if ( sub->lookup->lookup_type!=gpos_pair || sub->kc!=NULL ) {
15283 	PyErr_Format(PyExc_EnvironmentError, "%s is not a kerning pair subtable", subtablename );
15284 return( NULL );
15285     }
15286 
15287     if ( list1!=NULL ) {
15288 	first = GlyphsFromTuple(sf,list1);
15289 	second = GlyphsFromTuple(sf,list2);
15290     } else {
15291 	first = second = GlyphsFromSelection(fv);
15292     }
15293     if ( first==NULL || second==NULL ) {
15294 	free(second); free(first);
15295 return( NULL );
15296     }
15297     if ( sub->lookup->lookup_flags & pst_r2l ) {
15298 	left = second;
15299 	right = first;
15300     } else {
15301 	left = first;
15302 	right = second;
15303     }
15304     AutoKern2(sf, fv->active_layer,left,right, sub,
15305 	separation, minkern, touch, onlyCloser, height,
15306 	NULL,NULL);
15307     free(first);
15308     if ( first!=second )
15309 	free(second);
15310 Py_RETURN( self );
15311 }
15312 
PyFFFont_removeLookup(PyFF_Font * self,PyObject * args)15313 static PyObject *PyFFFont_removeLookup(PyFF_Font *self, PyObject *args) {
15314     SplineFont *sf;
15315     char *lookup;
15316     OTLookup *otl;
15317     int remove_acs = 0;
15318 
15319     if ( CheckIfFontClosed(self) )
15320 return (NULL);
15321     sf = self->fv->sf;
15322     if ( !PyArg_ParseTuple(args,"s|i", &lookup, &remove_acs ))
15323 return( NULL );
15324 
15325     otl = SFFindLookup(sf,lookup);
15326     if ( otl==NULL ) {
15327 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s exists", lookup );
15328 return( NULL );
15329     }
15330     SFRemoveLookup(sf,otl,remove_acs);
15331 Py_RETURN( self );
15332 }
15333 
PyFFFont_mergeLookups(PyFF_Font * self,PyObject * args)15334 static PyObject *PyFFFont_mergeLookups(PyFF_Font *self, PyObject *args) {
15335     SplineFont *sf;
15336     char *lookup1, *lookup2;
15337     OTLookup *otl1, *otl2;
15338     struct lookup_subtable *sub;
15339 
15340     if ( CheckIfFontClosed(self) )
15341 return (NULL);
15342     sf = self->fv->sf;
15343     if ( !PyArg_ParseTuple(args,"ss", &lookup1, &lookup2 ))
15344 return( NULL );
15345 
15346     otl1 = SFFindLookup(sf,lookup1);
15347     if ( otl1==NULL ) {
15348 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s exists", lookup1 );
15349 return( NULL );
15350     }
15351     otl2 = SFFindLookup(sf,lookup2);
15352     if ( otl2==NULL ) {
15353 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s exists", lookup2 );
15354 return( NULL );
15355     }
15356     if ( otl1->lookup_type != otl2->lookup_type ) {
15357 	PyErr_Format(PyExc_EnvironmentError, "When merging two lookups they must be of the same type, but %s and %s are not", lookup1, lookup2);
15358 return( NULL );
15359     }
15360     FLMerge(otl1,otl2);
15361 
15362     for ( sub = otl2->subtables; sub!=NULL; sub=sub->next )
15363 	sub->lookup = otl1;
15364     if ( otl1->subtables==NULL )
15365 	otl1->subtables = otl2->subtables;
15366     else {
15367 	for ( sub=otl1->subtables; sub->next!=NULL; sub=sub->next );
15368 	sub->next = otl2->subtables;
15369     }
15370     otl2->subtables = NULL;
15371     SFRemoveLookup(sf,otl2,0);
15372 Py_RETURN( self );
15373 }
15374 
PyFFFont_removeLookupSubtable(PyFF_Font * self,PyObject * args)15375 static PyObject *PyFFFont_removeLookupSubtable(PyFF_Font *self, PyObject *args) {
15376     SplineFont *sf;
15377     char *subtable;
15378     struct lookup_subtable *sub;
15379     int remove_acs = 0;
15380 
15381     if ( CheckIfFontClosed(self) )
15382 return (NULL);
15383     sf = self->fv->sf;
15384     if ( !PyArg_ParseTuple(args,"s|i", &subtable, &remove_acs ))
15385 return( NULL );
15386 
15387     sub = SFFindLookupSubtable(sf,subtable);
15388     if ( sub==NULL ) {
15389 	PyErr_Format(PyExc_EnvironmentError, "No subtable named %s exists", subtable );
15390 return( NULL );
15391     }
15392     SFRemoveLookupSubTable(sf,sub,remove_acs);
15393 Py_RETURN( self );
15394 }
15395 
PyFFFont_mergeLookupSubtables(PyFF_Font * self,PyObject * args)15396 static PyObject *PyFFFont_mergeLookupSubtables(PyFF_Font *self, PyObject *args) {
15397     SplineFont *sf;
15398     char *subtable1, *subtable2;
15399     struct lookup_subtable *sub1, *sub2;
15400 
15401     if ( CheckIfFontClosed(self) )
15402 return (NULL);
15403     sf = self->fv->sf;
15404     if ( !PyArg_ParseTuple(args,"ss", &subtable1, &subtable2 ))
15405 return( NULL );
15406 
15407     sub1 = SFFindLookupSubtable(sf,subtable1);
15408     if ( sub1==NULL ) {
15409 	PyErr_Format(PyExc_EnvironmentError, "No subtable named %s exists", subtable1 );
15410 return( NULL );
15411     }
15412     sub2 = SFFindLookupSubtable(sf,subtable2);
15413     if ( sub2==NULL ) {
15414 	PyErr_Format(PyExc_EnvironmentError, "No subtable named %s exists", subtable2 );
15415 return( NULL );
15416     }
15417     if ( sub1->lookup!=sub2->lookup ) {
15418 	PyErr_Format(PyExc_EnvironmentError, "When merging two lookup subtables they must be in the same lookup, but %s and %s are not", subtable1, subtable2);
15419 return( NULL );
15420     }
15421     SFSubTablesMerge(sf,sub1,sub2);
15422     SFRemoveLookupSubTable(sf,sub2,0);
15423 Py_RETURN( self );
15424 }
15425 
15426 /* OpenType lookup types: see 'enum otlookup_type' in splinefont.h */
15427 static struct flaglist lookup_types[] = {
15428     { "gsub_single", gsub_single },
15429     { "gsub_multiple", gsub_multiple },
15430     { "gsub_alternate", gsub_alternate },
15431     { "gsub_ligature", gsub_ligature },
15432     { "gsub_context", gsub_context },
15433     { "gsub_contextchain", gsub_contextchain },
15434     { "gsub_reversecchain", gsub_reversecchain },
15435     { "morx_indic", morx_indic },
15436     { "morx_context", morx_context },
15437     { "morx_insert", morx_insert },
15438     { "gpos_single", gpos_single },
15439     { "gpos_pair", gpos_pair },
15440     { "gpos_cursive", gpos_cursive },
15441     { "gpos_mark2base", gpos_mark2base },
15442     { "gpos_mark2ligature", gpos_mark2ligature },
15443     { "gpos_mark2mark", gpos_mark2mark },
15444     { "gpos_context", gpos_context },
15445     { "gpos_contextchain", gpos_contextchain },
15446     { "kern_statemachine", kern_statemachine },
15447     FLAGLIST_EMPTY /* Sentinel */
15448 };
15449 
15450 /* OpenType lookup flags: see 'enum pst_flags' in splinefont.h */
15451 static struct flaglist lookup_flags[] = {
15452     { "right_to_left", pst_r2l },
15453     { "ignore_bases", pst_ignorebaseglyphs },
15454     { "ignore_ligatures", pst_ignoreligatures },
15455     { "ignore_marks", pst_ignorecombiningmarks },
15456     /*{ "", pst_usemarkfilteringset },*/
15457     /*{ "", pst_markclass },*/
15458     /*{ "", pst_markset },*/
15459     { "right_2_left", pst_r2l },
15460     { "right2left", pst_r2l },
15461     FLAGLIST_EMPTY /* Sentinel */
15462 };
15463 
ParseLookupFlagsItem(SplineFont * sf,PyObject * flagstr)15464 static int ParseLookupFlagsItem(SplineFont *sf,PyObject *flagstr) {
15465     const char *str;
15466     int i;
15467 
15468     if ((str = PyUnicode_AsUTF8(flagstr)) == NULL) {
15469 	return( -1 );
15470     }
15471     for ( i=0; lookup_flags[i].name!=NULL; ++i )
15472 	if ( strcmp(lookup_flags[i].name,str)==0 ) {
15473 	    return( lookup_flags[i].flag );
15474 	}
15475 
15476     for ( i=1; i<sf->mark_class_cnt; ++i ) /* Start at 1 because class 0 is unused */
15477 	if ( strcmp(sf->mark_class_names[i],str)==0 ) {
15478 	    return( i<<8 );
15479 	}
15480 
15481     for ( i=0; i<sf->mark_set_cnt; ++i )
15482 	if ( strcmp(sf->mark_set_names[i],str)==0 ) {
15483 	    return( (i<<16) | pst_usemarkfilteringset );
15484 	}
15485 
15486     PyErr_Format(PyExc_ValueError, "Unknown lookup flag %s", str );
15487     return( -1 );
15488 }
15489 
ParseLookupFlags(SplineFont * sf,PyObject * flagtuple)15490 static int ParseLookupFlags(SplineFont *sf,PyObject *flagtuple) {
15491     int i, flags=0, cnt, temp;
15492 
15493     if ( PyLong_Check(flagtuple))
15494 return( PyLong_AsLong(flagtuple));
15495     if ( PyUnicode_Check(flagtuple))
15496 return( ParseLookupFlagsItem(sf,flagtuple));
15497     cnt = PySequence_Size(flagtuple);
15498     if ( cnt==-1 )
15499 return( -1 );
15500     for ( i=0; i<cnt; ++i ) {
15501 	PyObject *obj = PySequence_GetItem(flagtuple,i);
15502 	temp = ParseLookupFlagsItem(sf,obj);
15503 	Py_XDECREF(obj);
15504 	if ( temp==-1 )
15505 return( -1 );
15506 	flags |= temp;
15507     }
15508 return( flags );
15509 }
15510 
15511 #define BAD_FEATURE_LIST ((FeatureScriptLangList*)-1)
15512 
PyParseFeatureList(PyObject * tuple)15513 static FeatureScriptLangList *PyParseFeatureList(PyObject *tuple) {
15514     FeatureScriptLangList *flhead=NULL, *fltail, *fl;
15515     struct scriptlanglist *sltail, *sl;
15516     int f,s,l, cnt;
15517     int wasmac;
15518     PyObject *scripts, *langs;
15519 
15520     if ( !PySequence_Check(tuple)) {
15521 	PyErr_Format(PyExc_TypeError, "A feature list is composed of a tuple of tuples" );
15522 return( (FeatureScriptLangList *) -1 );
15523     }
15524     cnt = PySequence_Size(tuple);
15525 
15526     for ( f=0; f<cnt; ++f ) {
15527 	PyObject *subs = PySequence_GetItem(tuple,f);
15528 	if ( !PySequence_Check(subs)) {
15529 	    PyErr_Format(PyExc_TypeError, "A feature list is composed of a tuple of tuples" );
15530 	    FeatureScriptLangListFree(flhead);
15531 return( BAD_FEATURE_LIST );
15532 	} else if ( PySequence_Size(subs)!=2 ) {
15533 	    PyErr_Format(PyExc_TypeError, "A feature list is composed of a tuple of tuples each containing two elements");
15534 	    FeatureScriptLangListFree(flhead);
15535 return( BAD_FEATURE_LIST );
15536 	} else if ( !PyUnicode_Check(PySequence_GetItem(subs,0)) ||
15537 		!PySequence_Check(PySequence_GetItem(subs,1))) {
15538 	    PyErr_Format(PyExc_TypeError, "Bad type for argument");
15539 	    FeatureScriptLangListFree(flhead);
15540 return( BAD_FEATURE_LIST );
15541 	}
15542 	fl = chunkalloc(sizeof(FeatureScriptLangList));
15543 	fl->featuretag = StrObjToTag(PySequence_GetItem(subs,0),&wasmac);
15544 	if ( fl->featuretag == BAD_TAG ) {
15545 	    free(fl);
15546 	    FeatureScriptLangListFree(flhead);
15547 return( BAD_FEATURE_LIST );
15548 	}
15549 	fl->ismac = wasmac;
15550 	if ( flhead==NULL )
15551 	    flhead = fl;
15552 	else
15553 	    fltail->next = fl;
15554 	fltail = fl;
15555 	scripts = PySequence_GetItem(subs,1);
15556 	if ( !PySequence_Check(scripts)) {
15557 	    PyErr_Format(PyExc_TypeError, "A script list is composed of a tuple of tuples" );
15558 	    FeatureScriptLangListFree(flhead);
15559 return( BAD_FEATURE_LIST );
15560 	} else if ( PySequence_Size(scripts)==0 ) {
15561 	    PyErr_Format(PyExc_TypeError, "No scripts specified for feature %s", PyBytes_AsString(PySequence_GetItem(subs,0)));
15562         FeatureScriptLangListFree(flhead);
15563 return( BAD_FEATURE_LIST );
15564 	}
15565 	sltail = NULL;
15566 	for ( s=0; s<PySequence_Size(scripts); ++s ) {
15567 	    PyObject *scriptsubs = PySequence_GetItem(scripts,s);
15568 	    if ( !PySequence_Check(scriptsubs)) {
15569 		PyErr_Format(PyExc_TypeError, "A script list is composed of a tuple of tuples" );
15570 		FeatureScriptLangListFree(flhead);
15571 return( BAD_FEATURE_LIST );
15572 	    } else if ( PySequence_Size(scriptsubs)!=2 ) {
15573 		PyErr_Format(PyExc_TypeError, "A script list is composed of a tuple of tuples each containing two elements");
15574 		FeatureScriptLangListFree(flhead);
15575 return( BAD_FEATURE_LIST );
15576 	    } else if ( !PyUnicode_Check(PySequence_GetItem(scriptsubs,0)) ||
15577 		    !PySequence_Check(PySequence_GetItem(scriptsubs,1))) {
15578 		PyErr_Format(PyExc_TypeError, "Bad type for argument");
15579 		FeatureScriptLangListFree(flhead);
15580 return( BAD_FEATURE_LIST );
15581 	    }
15582 	    sl = chunkalloc(sizeof(struct scriptlanglist));
15583 	    sl->script = StrObjToTag(PySequence_GetItem(scriptsubs,0),NULL);
15584 	    if ( sl->script==BAD_TAG ) {
15585 		free(sl);
15586 		FeatureScriptLangListFree(flhead);
15587 return( BAD_FEATURE_LIST );
15588 	    }
15589 	    if ( sltail==NULL )
15590 		fl->scripts = sl;
15591 	    else
15592 		sltail->next = sl;
15593 	    sltail = sl;
15594 	    langs = PySequence_GetItem(scriptsubs,1);
15595 	    if ( PyUnicode_Check(langs) ) {
15596 		uint32 lang = StrObjToTag(langs,NULL);
15597 		if ( lang==BAD_TAG ) {
15598 		    FeatureScriptLangListFree(flhead);
15599 return( BAD_FEATURE_LIST );
15600 		}
15601 		sl->lang_cnt = 1;
15602 		sl->langs[0] = lang;
15603 	    } else if ( !PySequence_Check(langs)) {
15604 		PyErr_Format(PyExc_TypeError, "A language list is composed of a tuple of strings" );
15605 		FeatureScriptLangListFree(flhead);
15606 return( BAD_FEATURE_LIST );
15607 	    } else if ( PySequence_Size(langs)==0 ) {
15608 		sl->lang_cnt = 1;
15609 		sl->langs[0] = DEFAULT_LANG;
15610 	    } else {
15611 		sl->lang_cnt = PySequence_Size(langs);
15612 		if ( sl->lang_cnt>MAX_LANG )
15613 		    sl->morelangs = malloc((sl->lang_cnt-MAX_LANG)*sizeof(uint32));
15614 		for ( l=0; l<sl->lang_cnt; ++l ) {
15615 		    uint32 lang = StrObjToTag(PySequence_GetItem(langs,l),NULL);
15616 		    if ( lang==BAD_TAG ) {
15617 			FeatureScriptLangListFree(flhead);
15618 return( BAD_FEATURE_LIST );
15619 		    }
15620 		    if ( l<MAX_LANG )
15621 			sl->langs[l] = lang;
15622 		    else
15623 			sl->morelangs[l-MAX_LANG] = lang;
15624 		}
15625 	    }
15626 	}
15627     }
15628 return( flhead );
15629 }
15630 
PyFFFont_addLookup(PyFF_Font * self,PyObject * args)15631 static PyObject *PyFFFont_addLookup(PyFF_Font *self, PyObject *args) {
15632     SplineFont *sf;
15633     OTLookup *otl, *after = NULL;
15634     int itype;
15635     char *lookup_str, *type, *after_str=NULL;
15636     PyObject *flagtuple=NULL, *featlist;
15637     int flags;
15638     FeatureScriptLangList *fl;
15639 
15640     if ( CheckIfFontClosed(self) )
15641 return (NULL);
15642     sf = self->fv->sf;
15643     if ( !PyArg_ParseTuple(args,"ssOO|s", &lookup_str, &type, &flagtuple, &featlist, &after_str ))
15644 return( NULL );
15645 
15646     otl = SFFindLookup(sf,lookup_str);
15647     if ( otl!=NULL ) {
15648 	PyErr_Format(PyExc_EnvironmentError, "A lookup named %s already exists", lookup_str );
15649 return( NULL );
15650     }
15651     if ( after_str!=NULL ) {
15652 	after = SFFindLookup(sf,after_str);
15653 	if ( after==NULL ) {
15654 	    PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", after_str );
15655 return( NULL );
15656 	}
15657     }
15658 
15659     itype = FlagsFromString(type,lookup_types,"lookup type");
15660     if ( itype==FLAG_UNKNOWN )
15661 return( NULL );
15662 
15663     if (flagtuple == Py_None || flagtuple == NULL)
15664     flags = 0;
15665     else
15666     flags = ParseLookupFlags(sf,flagtuple);
15667     if ( flags==-1 )
15668 return( NULL );
15669 
15670     fl = PyParseFeatureList(featlist);
15671     if ( fl==BAD_FEATURE_LIST )
15672 return( NULL );
15673 
15674     if ( after!=NULL && (after->lookup_type>=gpos_start)!=(itype>=gpos_start) ) {
15675 	PyErr_Format(PyExc_EnvironmentError, "After lookup, %s, is in a different table", after_str );
15676 	FeatureScriptLangListFree(fl);
15677 return( NULL );
15678     }
15679 
15680     if ( sf->cidmaster ) sf = sf->cidmaster;
15681 
15682     otl = chunkalloc(sizeof(OTLookup));
15683     if ( after!=NULL ) {
15684 	otl->next = after->next;
15685 	after->next = otl;
15686     } else if ( itype>=gpos_start ) {
15687 	otl->next = sf->gpos_lookups;
15688 	sf->gpos_lookups = otl;
15689     } else {
15690 	otl->next = sf->gsub_lookups;
15691 	sf->gsub_lookups = otl;
15692     }
15693     otl->lookup_type = itype;
15694     otl->lookup_flags = flags;
15695     otl->lookup_name = copy(lookup_str);
15696     otl->features = fl;
15697     if ( fl!=NULL && (fl->featuretag==CHR('l','i','g','a') || fl->featuretag==CHR('r','l','i','g')))
15698 	otl->store_in_afm = true;
15699 Py_RETURN( self );
15700 }
15701 
PyFFFont_importLookups(PyFF_Font * self,PyObject * args)15702 static PyObject *PyFFFont_importLookups(PyFF_Font *self, PyObject *args) {
15703     SplineFont *sf;
15704     SplineFont *othersf;
15705     PyObject *lookup_list;
15706     PyFF_Font *otherfont;
15707     const char *lookup_str, *before_str=NULL;
15708     OTLookup *otl, *before, **list;
15709     int i;
15710 
15711     if ( CheckIfFontClosed(self) )
15712 return (NULL);
15713     sf = self->fv->sf;
15714     if ( !PyArg_ParseTuple(args,"OO|s", ((PyObject **)&otherfont), &lookup_list, &before_str))
15715 return( NULL );
15716     if ( !PyType_IsSubtype(&PyFF_FontType, Py_TYPE(otherfont)) ) {
15717 	PyErr_Format(PyExc_TypeError,"First argument must be a fontforge font");
15718 return( NULL );
15719     }
15720     if ( CheckIfFontClosed(otherfont) )
15721 return (NULL);
15722 
15723     othersf = otherfont->fv->sf;
15724 
15725     list = NULL;
15726     before = NULL;
15727     if ( before_str!=NULL )
15728 	before = SFFindLookup(sf,before_str);
15729     if ( PyUnicode_Check(lookup_list)) {
15730 	if ((lookup_str = PyUnicode_AsUTF8(lookup_list)) == NULL) {
15731 	    return NULL;
15732 	}
15733 	otl = SFFindLookup(othersf,lookup_str);
15734 	if ( otl==NULL ) {
15735 	    PyErr_Format(PyExc_EnvironmentError, "No lookup named %s exists in %s.", lookup_str, othersf->fontname );
15736 return( NULL );
15737 	}
15738 	list = calloc(2,sizeof(OTLookup *));
15739 	list[0] = otl;
15740     } else if ( PySequence_Check(lookup_list)) {
15741 	int subcnt = PySequence_Size(lookup_list);
15742 	list = calloc(subcnt+1,sizeof(OTLookup *));
15743 	for ( i=0; i<subcnt; ++i ) {
15744 	    PyObject *str = PySequence_GetItem(lookup_list,i);
15745 	    if ((lookup_str = PyUnicode_AsUTF8(str)) == NULL) {
15746 		Py_DECREF(str);
15747 		free(list);
15748 		return NULL;
15749 	    }
15750 	    otl = SFFindLookup(othersf,lookup_str);
15751 	    Py_DECREF(str);
15752 	    if ( otl==NULL ) {
15753 		PyErr_Format(PyExc_EnvironmentError, "No lookup named %s exists in %s.", lookup_str, othersf->fontname );
15754 		free(list);
15755 return( NULL );
15756 	    }
15757 	    list[i] = otl;
15758 	}
15759     } else {
15760 	PyErr_Format(PyExc_TypeError, "Unexpected type" );
15761         free(list);
15762 return( NULL );
15763     }
15764     OTLookupsCopyInto(sf,othersf,list,before);
15765     free(list);
15766 Py_RETURN( self );
15767 }
15768 
PyFFFont_lookupSetFeatureList(PyFF_Font * self,PyObject * args)15769 static PyObject *PyFFFont_lookupSetFeatureList(PyFF_Font *self, PyObject *args) {
15770     SplineFont *sf;
15771     OTLookup *otl;
15772     char *lookup;
15773     PyObject *featlist;
15774     FeatureScriptLangList *fl;
15775 
15776     if ( CheckIfFontClosed(self) )
15777 return (NULL);
15778     sf = self->fv->sf;
15779     if ( !PyArg_ParseTuple(args,"sO", &lookup, &featlist ))
15780 return( NULL );
15781 
15782     otl = SFFindLookup(sf,lookup);
15783     if ( otl==NULL ) {
15784 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", lookup );
15785 return( NULL );
15786     }
15787 
15788     fl = PyParseFeatureList(featlist);
15789     if ( fl==BAD_FEATURE_LIST )
15790 return( NULL );
15791 
15792     FeatureScriptLangListFree(otl->features);
15793     otl->features = fl;
15794 Py_RETURN( self );
15795 }
15796 
PyFFFont_lookupSetFlags(PyFF_Font * self,PyObject * args)15797 static PyObject *PyFFFont_lookupSetFlags(PyFF_Font *self, PyObject *args) {
15798     SplineFont *sf;
15799     OTLookup *otl;
15800     char *lookup;
15801     PyObject *flagtuple;
15802     int flags;
15803 
15804     if ( CheckIfFontClosed(self) )
15805 return (NULL);
15806     sf = self->fv->sf;
15807     if ( !PyArg_ParseTuple(args,"sO", &lookup, &flagtuple ))
15808 return( NULL );
15809 
15810     otl = SFFindLookup(sf,lookup);
15811     if ( otl==NULL ) {
15812 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", lookup );
15813 return( NULL );
15814     }
15815 
15816     flags = ParseLookupFlags(sf,flagtuple);
15817     if ( flags==-1 )
15818 return( NULL );
15819 
15820     otl->lookup_flags = flags;
15821 Py_RETURN( self );
15822 }
15823 
PyFFFont_lookupSetStoreLigatureInAfm(PyFF_Font * self,PyObject * args)15824 static PyObject *PyFFFont_lookupSetStoreLigatureInAfm(PyFF_Font *self, PyObject *args) {
15825     SplineFont *sf;
15826     OTLookup *otl;
15827     char *lookup;
15828     int store_it;
15829 
15830     if ( CheckIfFontClosed(self) )
15831 return (NULL);
15832     sf = self->fv->sf;
15833     if ( !PyArg_ParseTuple(args,"si", &lookup, &store_it ))
15834 return( NULL );
15835 
15836     otl = SFFindLookup(sf,lookup);
15837     if ( otl==NULL ) {
15838 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", lookup );
15839 return( NULL );
15840     }
15841     otl->store_in_afm = store_it;
15842 Py_RETURN( self );
15843 }
15844 
PyFFFont_getLookupInfo(PyFF_Font * self,PyObject * args)15845 static PyObject *PyFFFont_getLookupInfo(PyFF_Font *self, PyObject *args) {
15846     SplineFont *sf;
15847     OTLookup *otl;
15848     char *lookup;
15849     const char *type;
15850     int i, cnt;
15851     PyObject *flags_tuple;
15852     FeatureScriptLangList *fl;
15853     struct scriptlanglist *sl;
15854     int fcnt, scnt, l;
15855     PyObject *farray, *sarray, *larray;
15856 
15857     if ( CheckIfFontClosed(self) )
15858 return (NULL);
15859     sf = self->fv->sf;
15860     if ( !PyArg_ParseTuple(args,"s", &lookup ))
15861 return( NULL );
15862 
15863     otl = SFFindLookup(sf,lookup);
15864     if ( otl==NULL ) {
15865 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", lookup );
15866 return( NULL );
15867     }
15868 
15869     for ( i=0; lookup_types[i].name!=NULL ; ++i )
15870 	if ( (uint32)lookup_types[i].flag == otl->lookup_type )
15871     break;
15872     type = lookup_types[i].name;
15873 
15874     cnt = ( otl->lookup_flags&0xff00 )!=0;
15875     for ( i=0; i<5; ++i )
15876 	if ( otl->lookup_flags&(1<<i) )
15877 	    ++cnt;
15878     flags_tuple = PyTuple_New(cnt);
15879     cnt = 0;
15880     if ( otl->lookup_flags&0xff00 )
15881 	PyTuple_SetItem(flags_tuple,cnt++,Py_BuildValue("s",sf->mark_class_names[ (otl->lookup_flags&0xff00)>>8 ]));
15882     if ( otl->lookup_flags&pst_usemarkfilteringset )
15883 	PyTuple_SetItem(flags_tuple,cnt++,Py_BuildValue("s",sf->mark_set_names[ (otl->lookup_flags>>16)&0xffff ]));
15884     for ( i=0; i<4; ++i )
15885 	if ( otl->lookup_flags&(1<<i) )
15886 	    PyTuple_SetItem(flags_tuple,cnt++,Py_BuildValue("s",lookup_flags[i].name));
15887 
15888     for ( fl=otl->features, fcnt=0; fl!=NULL; fl=fl->next, ++fcnt );
15889     farray = PyTuple_New(fcnt);
15890     for ( fl=otl->features, fcnt=0; fl!=NULL; fl=fl->next, ++fcnt ) {
15891 	for ( sl=fl->scripts, scnt=0; sl!=NULL; sl=sl->next, ++scnt );
15892 	sarray = PyTuple_New(scnt);
15893 	for ( sl=fl->scripts, scnt=0; sl!=NULL; sl=sl->next, ++scnt ) {
15894 	    larray = PyTuple_New(sl->lang_cnt);
15895 	    for ( l=0; l<sl->lang_cnt; ++l )
15896 		PyTuple_SetItem(larray,l,TagToPythonString(l<MAX_LANG?sl->langs[l]:sl->morelangs[l-MAX_LANG],false));
15897 	    PyTuple_SetItem(sarray,scnt,Py_BuildValue("(OO)",
15898 		    TagToPythonString(sl->script,false),larray));
15899 	}
15900 	PyTuple_SetItem(farray,fcnt,Py_BuildValue("(OO)",
15901 		TagToPythonString(fl->featuretag,fl->ismac),sarray));
15902     }
15903 return( Py_BuildValue("(sOO)",type,flags_tuple,farray) );
15904 }
15905 
PyFFFont_addLookupSubtable(PyFF_Font * self,PyObject * args)15906 static PyObject *PyFFFont_addLookupSubtable(PyFF_Font *self, PyObject *args) {
15907     SplineFont *sf;
15908     char *lookup, *subtable, *after_str=NULL;
15909     OTLookup *otl;
15910 
15911     if ( CheckIfFontClosed(self) )
15912 return (NULL);
15913     sf = self->fv->sf;
15914     if ( !PyArg_ParseTuple(args,"ss|s", &lookup, &subtable, &after_str ))
15915 return( NULL );
15916 
15917     otl = SFFindLookup(sf,lookup);
15918     if ( otl!=NULL ) {
15919 	if ( otl->lookup_type==gsub_context || otl->lookup_type==gsub_contextchain ||
15920 		otl->lookup_type==gpos_context || otl->lookup_type==gpos_contextchain ||
15921 		otl->lookup_type==gsub_reversecchain ) {
15922 	    PyErr_Format(PyExc_TypeError, "Use addContextualSubtable to create a subtable in %s.", lookup );
15923 return( NULL );
15924 	}
15925     }
15926 
15927     if ( addLookupSubtable(sf, lookup, subtable, after_str)==NULL )
15928 return( NULL );
15929 
15930 Py_RETURN( self );
15931 }
15932 
15933 static const char *contextchain_keywords[] = {
15934 	"lookup", "subtable", "type", "rule",
15935 	"afterSubtable",
15936 	"bclasses", "mclasses", "fclasses",
15937 	"bclassnames", "mclassnames", "fclassnames", NULL };
15938 
PyFFFont_addContextualSubtable(PyFF_Font * self,PyObject * args,PyObject * keywds)15939 static PyObject *PyFFFont_addContextualSubtable(PyFF_Font *self, PyObject *args, PyObject *keywds) {
15940     SplineFont *sf;
15941     char *lookup, *subtable, *after_str=NULL, *type, *rule;
15942     PyObject *bclasses=NULL, *mclasses=NULL, *fclasses=NULL;
15943     PyObject *bclassnames=NULL, *mclassnames=NULL, *fclassnames=NULL;
15944     struct lookup_subtable *new_subtable;
15945     FPST *fpst;
15946     OTLookup *otl;
15947     enum fpossub_format format;
15948     int bcnt=0, mcnt=0, fcnt=0;
15949     char **backclasses=NULL, **matchclasses=NULL, **forclasses=NULL;
15950     char **backclassnames=NULL, **matchclassnames=NULL, **forclassnames=NULL;
15951     int is_warning;
15952     char *msg;
15953 
15954     if ( CheckIfFontClosed(self) )
15955 return (NULL);
15956     sf = self->fv->sf;
15957     if ( !PyArg_ParseTupleAndKeywords(args,keywds,"ssss|sOOOOOO", (char **)contextchain_keywords,
15958 	    &lookup, &subtable, &type, &rule,
15959 	    &after_str, &bclasses, &mclasses, &fclasses,
15960 	    &bclassnames, &mclassnames, &fclassnames))
15961 return( NULL );
15962 
15963     if ( strcasecmp(type,"glyph")==0 )
15964 	format = pst_glyphs;
15965     else if ( strcasecmp(type,"class")==0 )
15966 	format = pst_class;
15967     else if ( strcasecmp(type,"coverage")==0 )
15968 	format = pst_coverage;
15969     else if ( strcasecmp(type,"reversecoverage")==0 )
15970 	format = pst_reversecoverage;
15971     else {
15972 	PyErr_Format(PyExc_TypeError, "Bad format, %s, for contextual lookup (must be one of \"glyph\", \"class\" or \"coverage\" (or, rarely, \"reversecoverage\"))", type );
15973 return( NULL );
15974     }
15975 
15976     otl = SFFindLookup(sf,lookup);
15977     if ( otl!=NULL ) {
15978 	if ( otl->lookup_type!=gsub_context && otl->lookup_type!=gsub_contextchain &&
15979 		otl->lookup_type!=gpos_context && otl->lookup_type!=gpos_contextchain &&
15980 		otl->lookup_type!=gsub_reversecchain ) {
15981 	    PyErr_Format(PyExc_TypeError, "The lookup, %s, may not contain a contextual subtable.\nUse addLookupSubtable() instead", lookup );
15982 return( NULL );
15983 	}
15984 	if ( otl->lookup_type == gsub_reversecchain && format!=pst_reversecoverage ) {
15985 	    PyErr_Format(PyExc_TypeError, "Bad format, %s, for reverse context chaining lookup (must be \"reversecoverage\")", type );
15986 return( NULL );
15987 	}
15988 	if ( otl->lookup_type != gsub_reversecchain && format==pst_reversecoverage ) {
15989 	    PyErr_Format(PyExc_TypeError, "Bad format, %s, for this lookup (must be one of \"glyph\", \"class\" or \"coverage\")", type );
15990 return( NULL );
15991 	}
15992     }
15993 
15994     if ( format==pst_class && mclasses==NULL ) {
15995 	PyErr_Format(PyExc_TypeError, "When using the class format, you must specify some classes" );
15996 return( NULL );
15997     } else if ( format!=pst_class &&
15998 	    (mclasses!=NULL || bclasses!=NULL || fclasses!=NULL ||
15999 	     mclassnames!=NULL || bclassnames!=NULL || fclassnames!=NULL )) {
16000 	PyErr_Format(PyExc_TypeError, "When not using the class format, you may not specify any classes" );
16001 return( NULL );
16002     } else if ( otl!=NULL && ( otl->lookup_type==gsub_context || otl->lookup_type==gpos_context ) &&
16003 	    ( bclasses!=NULL || fclasses!=NULL )) {
16004 	PyErr_Format(PyExc_TypeError, "You may only specify backtracking or forward-looking classes when building a contextual chaining lookup (this one is merely contextual)." );
16005 return( NULL );
16006     } else if ( (mclasses==NULL && mclassnames!=NULL) ||
16007 		(bclasses==NULL && bclassnames!=NULL) ||
16008 		(fclasses==NULL && fclassnames!=NULL) ) {
16009 	PyErr_Format(PyExc_TypeError, "If you specify class names, you must also specify some classes..." );
16010 return( NULL );
16011     } else if ( (mclasses!=NULL && mclassnames!=NULL && PySequence_Size(mclasses)!=PySequence_Size(mclassnames)) ||
16012 		(bclasses!=NULL && bclassnames!=NULL && PySequence_Size(bclasses)!=PySequence_Size(bclassnames)) ||
16013 		(fclasses!=NULL && fclassnames!=NULL && PySequence_Size(fclasses)!=PySequence_Size(fclassnames)) ) {
16014 	PyErr_Format(PyExc_TypeError, "When you specify class names there must be as many names as there are classes" );
16015 return( NULL );
16016     } else if ( format==pst_class ) {
16017 	mcnt = ParseClassNames(mclasses,&matchclasses);
16018 	if ( mcnt==-1 ) {
16019 	    PyErr_Format(PyExc_TypeError, "Bad match class" );
16020 return( NULL );
16021 	}
16022 	if ( mclassnames!=NULL ) {
16023 	    if ((matchclassnames = GlyphNameArrayFromTuple(mclassnames))== NULL ) {
16024 		PyErr_Format(PyExc_TypeError, "Bad set of class names for mclassname." );
16025 return( NULL );
16026 	    }
16027 	}
16028 	if ( bclasses!=NULL ) {
16029 	    bcnt = ParseClassNames(bclasses,&backclasses);
16030 	    if ( bcnt==-1 ) {
16031 		PyErr_Format(PyExc_TypeError, "Bad backtrack class" );
16032 return( NULL );
16033 	    }
16034 	    if ( bclassnames!=NULL ) {
16035 		if ((backclassnames = GlyphNameArrayFromTuple(bclassnames))== NULL ) {
16036 		    PyErr_Format(PyExc_TypeError, "Bad set of class names for bclassname." );
16037 return( NULL );
16038 		}
16039 	    }
16040 	}
16041 	if ( fclasses!=NULL ) {
16042 	    fcnt = ParseClassNames(fclasses,&forclasses);
16043 	    if ( fcnt==-1 ) {
16044 		PyErr_Format(PyExc_TypeError, "Bad forward class" );
16045 return( NULL );
16046 	    }
16047 	    if ( fclassnames!=NULL ) {
16048 		if ((forclassnames = GlyphNameArrayFromTuple(fclassnames))== NULL ) {
16049 		    PyErr_Format(PyExc_TypeError, "Bad set of class names for fclassname." );
16050 return( NULL );
16051 		}
16052 	    }
16053 	}
16054     }
16055 
16056     new_subtable = addLookupSubtable(sf, lookup, subtable, after_str);
16057     if ( new_subtable==NULL ) {
16058 	free(backclassnames); free(matchclasses); free(forclasses);
16059 return( NULL );
16060     }
16061     fpst = chunkalloc(sizeof(FPST));
16062     fpst->subtable = new_subtable;
16063     new_subtable->fpst = fpst;
16064     fpst->format = format;
16065     fpst->type = otl->lookup_type==gsub_reversecchain ? pst_reversesub :
16066 		otl->lookup_type==gsub_context ? pst_contextsub :
16067 		otl->lookup_type==gsub_contextchain ? pst_chainsub :
16068 		otl->lookup_type==gpos_context ? pst_contextpos :
16069 		  pst_chainpos;
16070     fpst->next = sf->possub;
16071     sf->possub = fpst;
16072     fpst->bccnt = bcnt;
16073     fpst->bclass = backclasses;
16074     if ( backclasses!=NULL && backclassnames==NULL )
16075 	fpst->bclassnames = calloc(bcnt,sizeof(char *));
16076     else
16077 	fpst->bclassnames = backclassnames;
16078     fpst->nccnt = mcnt;
16079     fpst->nclass = matchclasses;
16080     if ( matchclasses!=NULL && matchclassnames==NULL )
16081 	fpst->nclassnames = calloc(mcnt,sizeof(char *));
16082     else
16083 	fpst->nclassnames = matchclassnames;
16084     fpst->fccnt = fcnt;
16085     fpst->fclass = forclasses;
16086     if ( forclasses!=NULL && forclassnames==NULL )
16087 	fpst->fclassnames = calloc(fcnt,sizeof(char *));
16088     else
16089 	fpst->fclassnames = forclassnames;
16090     fpst->rule_cnt = 1;
16091     fpst->rules = calloc(1,sizeof(struct fpst_rule));
16092 
16093     msg = FPSTRule_From_Str( sf,fpst,fpst->rules,rule,&is_warning);
16094     if ( is_warning ) {
16095 	LogError("%s",msg);
16096 	free(msg);
16097 	msg = NULL;
16098     }
16099     if ( msg!=NULL ) {
16100 	PyErr_Format(PyExc_TypeError, "%s", msg );
16101 	free(msg);
16102 return( NULL );
16103     }
16104 
16105 Py_RETURN( self );
16106 }
16107 
PyFFFont_getLookupSubtables(PyFF_Font * self,PyObject * args)16108 static PyObject *PyFFFont_getLookupSubtables(PyFF_Font *self, PyObject *args) {
16109     SplineFont *sf;
16110     char *lookup;
16111     OTLookup *otl;
16112     struct lookup_subtable *sub;
16113     int cnt;
16114     PyObject *tuple;
16115 
16116     if ( CheckIfFontClosed(self) )
16117 return (NULL);
16118     sf = self->fv->sf;
16119     if ( !PyArg_ParseTuple(args,"s", &lookup ))
16120 return( NULL );
16121 
16122     otl = SFFindLookup(sf,lookup);
16123     if ( otl==NULL ) {
16124 	PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", lookup );
16125 return( NULL );
16126     }
16127     for ( sub = otl->subtables, cnt=0; sub!=NULL; sub=sub->next, ++cnt );
16128     tuple = PyTuple_New(cnt);
16129     for ( sub = otl->subtables, cnt=0; sub!=NULL; sub=sub->next, ++cnt )
16130 	PyTuple_SetItem(tuple,cnt,Py_BuildValue("s",sub->subtable_name));
16131 return( tuple );
16132 }
16133 
PyFFFont_getLookupSubtableAnchorClasses(PyFF_Font * self,PyObject * args)16134 static PyObject *PyFFFont_getLookupSubtableAnchorClasses(PyFF_Font *self, PyObject *args) {
16135     SplineFont *sf;
16136     char *subtable;
16137     struct lookup_subtable *sub;
16138     AnchorClass *ac;
16139     int cnt;
16140     PyObject *tuple;
16141 
16142     if ( CheckIfFontClosed(self) )
16143 return (NULL);
16144     sf = self->fv->sf;
16145     if ( !PyArg_ParseTuple(args,"s", &subtable ))
16146 return( NULL );
16147 
16148     sub = SFFindLookupSubtable(sf,subtable);
16149     if ( sub==NULL ) {
16150 	PyErr_Format(PyExc_EnvironmentError, "No lookup subtable named %s", subtable );
16151 return( NULL );
16152     }
16153     for ( ac = sf->anchor, cnt=0; ac!=NULL; ac=ac->next )
16154 	if ( ac->subtable == sub )
16155 	    ++cnt;
16156     tuple = PyTuple_New(cnt);
16157     for ( ac = sf->anchor, cnt=0; ac!=NULL; ac=ac->next )
16158 	if ( ac->subtable == sub )
16159 	    PyTuple_SetItem(tuple,cnt++,Py_BuildValue("s",ac->name));
16160 return( tuple );
16161 }
16162 
PyFFFont_getLookupOfSubtable(PyFF_Font * self,PyObject * args)16163 static PyObject *PyFFFont_getLookupOfSubtable(PyFF_Font *self, PyObject *args) {
16164     SplineFont *sf;
16165     char *subtable;
16166     struct lookup_subtable *sub;
16167 
16168     if ( CheckIfFontClosed(self) )
16169 return (NULL);
16170     sf = self->fv->sf;
16171     if ( !PyArg_ParseTuple(args,"s", &subtable ))
16172 return( NULL );
16173 
16174     sub = SFFindLookupSubtable(sf,subtable);
16175     if ( sub==NULL ) {
16176 	PyErr_Format(PyExc_EnvironmentError, "No lookup subtable named %s", subtable );
16177 return( NULL );
16178     }
16179 return( Py_BuildValue("s", sub->lookup->lookup_name ));
16180 }
16181 
PyFFFont_getSubtableOfAnchor(PyFF_Font * self,PyObject * args)16182 static PyObject *PyFFFont_getSubtableOfAnchor(PyFF_Font *self, PyObject *args) {
16183     SplineFont *sf;
16184     char *anchorclass;
16185     AnchorClass *ac;
16186 
16187     if ( CheckIfFontClosed(self) )
16188 return (NULL);
16189     sf = self->fv->sf;
16190     if ( !PyArg_ParseTuple(args,"s", &anchorclass ))
16191 return( NULL );
16192 
16193     for ( ac=sf->anchor; ac!=NULL; ac=ac->next )
16194 	if ( strcmp(ac->name,anchorclass)==0 )
16195 return( Py_BuildValue("s", ac->subtable ? ac->subtable->subtable_name : ""));
16196 
16197     PyErr_Format(PyExc_EnvironmentError, "No anchor class named %s", anchorclass );
16198 return( NULL );
16199 }
16200 
PyFFFont_saveNamelist(PyFF_Font * self,PyObject * args)16201 static PyObject *PyFFFont_saveNamelist(PyFF_Font *self, PyObject *args) {
16202     FontViewBase *fv;
16203     char *filename;
16204     FILE *file;
16205 
16206     if ( CheckIfFontClosed(self) )
16207 return (NULL);
16208     fv = self->fv;
16209     if ( !PyArg_ParseTuple(args,"s", &filename ))
16210 return( NULL );
16211 
16212     file = fopen(filename,"w");
16213     if ( file==NULL ) {
16214 	PyErr_SetFromErrnoWithFilename(PyExc_IOError,filename);
16215 return(NULL);
16216     }
16217     FVB_MakeNamelist(fv, file);
16218     fclose(file);
16219 Py_RETURN_NONE;
16220 }
16221 
PyFFFont_replaceAll(PyFF_Font * self,PyObject * args)16222 static PyObject *PyFFFont_replaceAll(PyFF_Font *self, PyObject *args) {
16223     FontViewBase *fv;
16224     PyObject *srch, *rpl;
16225     SplineSet *srch_ss, *rpl_ss;
16226     double err = .01;
16227 
16228     if ( CheckIfFontClosed(self) )
16229 return (NULL);
16230     fv = self->fv;
16231     if ( !PyArg_ParseTuple(args,"OO|d", &srch, &rpl, &err ))
16232 return( NULL );
16233 
16234     if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(srch)) ) {
16235 	srch_ss = SSFromLayer((PyFF_Layer *) srch);
16236     } else if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(srch)) ) {
16237 	srch_ss = SSFromContour((PyFF_Contour *) srch, NULL);
16238     } else {
16239 	PyErr_Format(PyExc_TypeError, "Expected a contour or layer");
16240 return( NULL );
16241     }
16242     if ( PyErr_Occurred() != NULL ) {
16243 	return( NULL );
16244     }
16245 
16246     if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(rpl)) ) {
16247 	rpl_ss = SSFromLayer((PyFF_Layer *) rpl);
16248     } else if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(rpl)) ) {
16249 	rpl_ss = SSFromContour((PyFF_Contour *) rpl, NULL);
16250     } else {
16251 	PyErr_Format(PyExc_TypeError, "Expected a contour or layer");
16252 return( NULL );
16253     }
16254     if ( PyErr_Occurred() != NULL ) {
16255 	SplinePointListsFree(srch_ss);
16256 	return( NULL );
16257     }
16258 
16259     /* srch_ss and rpl_ss will be freed by ReplaceAll */
16260 return( Py_BuildValue( "i", FVReplaceAll(fv,srch_ss,rpl_ss,err,sv_reverse|sv_flips)));
16261 }
16262 
16263 /* Search flags: see 'enum search_flags' in baseviews.h */
16264 struct flaglist find_flags[] = {
16265     {"reverse", sv_reverse},
16266     {"flips", sv_flips},
16267     {"rotate", sv_rotate},
16268     {"scale", sv_scale},
16269     {"endpoints", sv_endpoints},
16270     FLAGLIST_EMPTY /* Sentinel */
16271 };
16272 
PyFFFont_find(PyFF_Font * self,PyObject * args)16273 static PyObject *PyFFFont_find(PyFF_Font *self, PyObject *args) {
16274     FontViewBase *fv;
16275     PyObject *srch;
16276     SplineSet *srch_ss;
16277     PyObject *pyflags = NULL;
16278     int flags = 0;
16279     double err = .01;
16280 
16281     if ( CheckIfFontClosed(self) )
16282 return (NULL);
16283     fv = self->fv;
16284     if ( !PyArg_ParseTuple(args,"O|dO", &srch, &err, &pyflags))
16285 return( NULL );
16286 
16287     if ( PyType_IsSubtype(&PyFF_LayerType, Py_TYPE(srch)) ) {
16288 	srch_ss = SSFromLayer((PyFF_Layer *) srch);
16289     } else if ( PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(srch)) ) {
16290 	srch_ss = SSFromContour((PyFF_Contour *) srch, NULL);
16291     } else {
16292 	PyErr_Format(PyExc_TypeError, "Expected a contour or layer");
16293 return( NULL );
16294     }
16295     if ( PyErr_Occurred() != NULL ) {
16296 	return( NULL );
16297     }
16298     if (pyflags) {
16299 	flags = FlagsFromTuple(pyflags, find_flags, "search flag");
16300 	if ( flags==FLAG_UNKNOWN )
16301 return( NULL );
16302     }
16303     else
16304 	flags = sv_reverse|sv_flips;
16305 
16306     /* srch_ss will be freed when the iterator dies */
16307 return( fontiter_New( self,false,SDFromContour(fv,srch_ss,err,flags)) );
16308 }
16309 
PyFFFont_glyphs(PyFF_Font * self,PyObject * args)16310 static PyObject *PyFFFont_glyphs(PyFF_Font *self, PyObject *args) {
16311     const char *type = "GID";
16312     int index;
16313 
16314     if ( CheckIfFontClosed(self) )
16315 return (NULL);
16316     if ( !PyArg_ParseTuple(args,"|s", &type ))
16317 return( NULL );
16318 
16319     if ( strcasecmp(type,"GID")==0 )
16320 	index = 3;
16321     else if ( strcasecmp(type,"encoding")==0 )
16322 	index = 4;
16323     else {
16324 	PyErr_Format(PyExc_TypeError, "Unexpected type");
16325 return( NULL );
16326     }
16327 
16328 return( fontiter_New( self,index,NULL) );
16329 }
16330 
16331 
PyFFFont_Save(PyFF_Font * self,PyObject * args)16332 static PyObject *PyFFFont_Save(PyFF_Font *self, PyObject *args) {
16333     char *filename = NULL;
16334     int localRevisionsToRetain = -1;
16335     char *locfilename = NULL;
16336     char *pt;
16337     FontViewBase *fv;
16338     int s2d=false;
16339 
16340     if ( CheckIfFontClosed(self) )
16341 	return(NULL);
16342     fv = self->fv;
16343 
16344     if ( !PyArg_ParseTuple(args,"|si", &filename, &localRevisionsToRetain ))
16345         return( NULL );
16346 
16347     if ( filename!=NULL )
16348     {
16349 	/* Save As - Filename was provided */
16350 	locfilename = utf82def_copy(filename);
16351 
16352 	pt = strrchr(locfilename,'.');
16353 	if ( pt!=NULL && strmatch(pt,".sfdir")==0 )
16354 	    s2d = true;
16355 
16356 	int rc = SFDWriteBakExtended( locfilename,
16357 				      fv->sf,fv->map,fv->normal,s2d,
16358 				      localRevisionsToRetain );
16359 	if ( !rc )
16360 	{
16361 	    PyErr_Format(PyExc_EnvironmentError, "Save As \"%s\" failed",locfilename);
16362 	    free(locfilename);
16363 	    return( NULL );
16364 	}
16365     }
16366     else
16367     {
16368 	/* Save - No filename provided */
16369 	if ( fv->cidmaster!=NULL && fv->cidmaster->filename!=NULL )
16370 	    filename = fv->cidmaster->filename;
16371 	else if ( fv->sf->mm!=NULL && fv->sf->mm->normal->filename!=NULL )
16372 	    filename = fv->sf->mm->normal->filename;
16373 	else if ( fv->sf->filename!=NULL )
16374 	    filename = fv->sf->filename;
16375 	else {
16376 	    SplineFont *sf = fv->cidmaster?fv->cidmaster:
16377 		    fv->sf->mm!=NULL?fv->sf->mm->normal:fv->sf;
16378 	    char *fn = sf->defbasefilename ? sf->defbasefilename : sf->fontname;
16379 	    locfilename = malloc((strlen(fn)+10));
16380 	    strcpy(locfilename,fn);
16381 	    if ( sf->defbasefilename!=NULL )
16382 		/* Don't add a default suffix, they've already told us what name to use */;
16383 	    else if ( fv->cidmaster!=NULL )
16384 		strcat(locfilename,"CID");
16385 	    else if ( sf->mm==NULL )
16386 		;
16387 	    else if ( sf->mm->apple )
16388 		strcat(locfilename,"Var");
16389 	    else
16390 		strcat(locfilename,"MM");
16391 	    strcat(locfilename,".sfd");
16392 	}
16393 	char* targetfilename = locfilename;
16394 	if ( !targetfilename )
16395 	    targetfilename = fv->sf->filename;
16396 
16397 	/**
16398 	 * If there are no existing backup files, don't start creating them here.
16399 	 * Otherwise, save as many as the user wants.
16400 	 */
16401 	int rc = SFDWriteBakExtended( targetfilename,
16402 				      fv->sf,fv->map,fv->normal,s2d,
16403 				      localRevisionsToRetain );
16404 	if ( !rc )
16405 	{
16406 	    PyErr_Format(PyExc_EnvironmentError, "Save failed");
16407         free(locfilename);
16408 	    return( NULL );
16409 	}
16410     }
16411 
16412     /* Save succeeded, do any post-save fixups.
16413      * Refer to _FVMenuSaveAs() in fontview.c
16414      */
16415     if ( locfilename!=NULL ) {
16416 	SplineFont *sf = fv->cidmaster?fv->cidmaster:fv->sf->mm!=NULL?fv->sf->mm->normal:fv->sf;
16417 	free(sf->filename);
16418 	sf->filename = copy(locfilename);
16419 	sf->save_to_dir = s2d;
16420 	free(sf->origname);
16421 	sf->origname = copy(locfilename);
16422 	sf->new = false;
16423 	if ( sf->mm!=NULL ) {
16424 	    int i;
16425 	    for ( i=0; i<sf->mm->instance_count; ++i ) {
16426 		free(sf->mm->instances[i]->filename);
16427 		sf->mm->instances[i]->filename = copy(locfilename);
16428 		free(sf->mm->instances[i]->origname);
16429 		sf->mm->instances[i]->origname = copy(locfilename);
16430 		sf->mm->instances[i]->new = false;
16431 	    }
16432 	}
16433 	SplineFontSetUnChanged(sf);
16434 
16435 	free(locfilename);
16436     }
16437 
16438 Py_RETURN( self );
16439 }
16440 
PyFFFont_revert(PyFF_Font * self,PyObject * UNUSED (args))16441 static PyObject *PyFFFont_revert(PyFF_Font *self, PyObject *UNUSED(args)) {
16442     FontViewBase *fv;
16443     if ( CheckIfFontClosed(self) )
16444 return (NULL);
16445     fv = self->fv;
16446     FVRevert(fv);
16447 Py_RETURN( self );
16448 }
16449 
PyFFFont_revertFromBackup(PyFF_Font * self,PyObject * UNUSED (args))16450 static PyObject *PyFFFont_revertFromBackup(PyFF_Font *self, PyObject *UNUSED(args)) {
16451     FontViewBase *fv;
16452     if ( CheckIfFontClosed(self) )
16453 return (NULL);
16454     fv = self->fv;
16455     FVRevertBackup(fv);
16456 Py_RETURN( self );
16457 }
16458 
16459 /* filename, bitmaptype,flags,resolution,mult-sfd-file,namelist */
16460 static const char *gen_keywords[] = { "filename", "bitmap_type", "flags", "bitmap_resolution",
16461 	"subfont_directory", "namelist", "layer", NULL };
16462 static const char *genttc_keywords[] = { "filename", "others", "bitmap_type", "flags",
16463 	"ttcflags", "namelist", "layer", NULL };
16464 struct flaglist gen_flags[] = {
16465     { "afm", fm_flag_afm },
16466     { "pfm", fm_flag_pfm },
16467     { "short-post", fm_flag_shortps },
16468     { "omit-instructions", fm_flag_nopshints },
16469     { "apple", fm_flag_apple },
16470     { "opentype", fm_flag_opentype },
16471     { "PfEd-comments", fm_flag_pfed_comments },
16472     { "PfEd-colors", fm_flag_pfed_colors },
16473     { "PfEd-lookups", fm_flag_pfed_lookups },
16474     { "PfEd-guides", fm_flag_pfed_guides },
16475     { "PfEd-background", fm_flag_pfed_layers },
16476     { "winkern", fm_flag_winkern },
16477     { "glyph-map-file", fm_flag_glyphmap },
16478     { "TeX-table", fm_flag_TeXtable },
16479     { "ofm", fm_flag_ofm },
16480     { "old-kern", fm_flag_applemode },
16481     { "symbol", fm_flag_symbol },
16482     { "dummy-dsig", fm_flag_dummyDSIG },
16483     { "dummy-DSIG", fm_flag_dummyDSIG },
16484     { "no-FFTM-table", fm_flag_nofftm },
16485     { "tfm", fm_flag_tfm },
16486     { "no-flex", fm_flag_noflex },
16487     { "no-hints", fm_flag_nottfhints },
16488     { "round", fm_flag_round },
16489     { "composites-in-afm", fm_flag_afmwithmarks },
16490     { "no-mac-names", fm_flag_nomacnames },
16491     FLAGLIST_EMPTY /* Sentinel */
16492 };
16493 /* Generate TrueType Collection flags: see 'enum ttc_flags' in splinefont.h */
16494 struct flaglist genttc_flags[] = {
16495     { "merge", ttc_flag_trymerge },
16496     { "cff", ttc_flag_cff },
16497     FLAGLIST_EMPTY /* Sentinel */
16498 };
16499 
PyFFFont_Generate(PyFF_Font * self,PyObject * args,PyObject * keywds)16500 static PyObject *PyFFFont_Generate(PyFF_Font *self, PyObject *args, PyObject *keywds) {
16501     char *filename;
16502     char *locfilename = NULL;
16503     FontViewBase *fv;
16504     PyObject *flags=NULL;
16505     int iflags = -1;
16506     int resolution = -1;
16507     const char *bitmaptype="";
16508     char *subfontdirectory=NULL, *namelist=NULL;
16509     NameList *rename_to = NULL;
16510     int layer;
16511     char *layer_str=NULL;
16512 
16513     if ( CheckIfFontClosed(self) )
16514 return (NULL);
16515     fv = self->fv;
16516     layer = fv->active_layer;
16517     if ( !PyArg_ParseTupleAndKeywords(args, keywds, "s|sOissi", (char **)gen_keywords,
16518 	    &filename, &bitmaptype, &flags, &resolution, &subfontdirectory,
16519 	    &namelist, &layer) ) {
16520 	PyErr_Clear();
16521 	if ( !PyArg_ParseTupleAndKeywords(args, keywds, "s|sOisss", (char **)gen_keywords,
16522 		&filename, &bitmaptype, &flags, &resolution, &subfontdirectory,
16523 		&namelist, &layer_str) )
16524 return( NULL );
16525 	layer = SFFindLayerIndexByName(fv->sf,layer_str);
16526 	if ( layer<0 )
16527 return( NULL );
16528     }
16529     if ( layer<0 || layer>=fv->sf->layer_cnt ) {
16530 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
16531 return( NULL );
16532     }
16533     if ( flags!=NULL ) {
16534 	iflags = FlagsFromTuple(flags,gen_flags,"generate flag");
16535 	if ( iflags==FLAG_UNKNOWN ) {
16536 return( NULL );
16537 	}
16538 	/* Legacy screw ups mean that opentype & apple bits don't mean what */
16539 	/*  I want them to. Python users should not see that, but fix it up */
16540 	/*  here */
16541 	if ( (iflags&0x80) && (iflags&0x10) )	/* Both */
16542 	    iflags &= ~0x10;
16543 	else if ( (iflags&0x80) && !(iflags&0x10)) /* Just opentype */
16544 	    iflags &= ~0x80;
16545 	else if ( !(iflags&0x80) && (iflags&0x10)) /* Just apple */
16546 	    /* This one's set already */;
16547 	else
16548 	    iflags |= 0x90;
16549     }
16550     if ( namelist!=NULL ) {
16551 	rename_to = NameListByName(namelist);
16552 	if ( rename_to==NULL ) {
16553 	    PyErr_Format(PyExc_EnvironmentError, "Unknown namelist");
16554 return( NULL );
16555 	}
16556     }
16557     locfilename = utf82def_copy(filename);
16558     if ( !GenerateScript(fv->sf,locfilename,bitmaptype,iflags,resolution,subfontdirectory,
16559 	    NULL,fv->normal==NULL?fv->map:fv->normal,rename_to,layer) ) {
16560 	PyErr_Format(PyExc_EnvironmentError, "Font generation failed");
16561 return( NULL );
16562     }
16563     free(locfilename);
16564 Py_RETURN( self );
16565 }
16566 
16567 
freesflist(struct sflist * list)16568 static void freesflist(struct sflist* list) {
16569     struct sflist *next;
16570     for( ; list != NULL; list=next ) {
16571 	next = list->next;
16572 
16573         free(list->sizes);
16574 	chunkfree(list, sizeof(struct sflist));
16575     }
16576     return;
16577 }
16578 
makesflist(PyFF_Font * font,enum bitmapformat bf)16579 static struct sflist *makesflist(PyFF_Font *font,enum bitmapformat bf) {
16580     struct sflist *ret;
16581 
16582     if ( CheckIfFontClosed(font) )
16583 return(NULL);
16584 
16585     ret = chunkalloc(sizeof( struct sflist ));
16586     ret->sf  = font->fv->sf;
16587     ret->map = font->fv->map;
16588 
16589     if ( bf==bf_ttf ) {
16590 	int cnt;
16591 	BDFFont *bdf;
16592 	for ( cnt=0, bdf=ret->sf->bitmaps; bdf!=NULL; bdf=bdf->next, ++cnt );
16593 	if ( cnt!=0 ) {
16594 	    ret->sizes = malloc((cnt+1)*sizeof(int32));
16595 	    ret->sizes[cnt] = 0;
16596 	    for ( cnt=0, bdf=ret->sf->bitmaps; bdf!=NULL; bdf=bdf->next, ++cnt ) {
16597 		ret->sizes[cnt] = (BDFDepth(bdf)<<16) | bdf->pixelsize;
16598 	    }
16599 	}
16600     }
16601 return( ret );
16602 }
16603 
PyFFFont_GenerateTTC(PyFF_Font * self,PyObject * args,PyObject * keywds)16604 static PyObject *PyFFFont_GenerateTTC(PyFF_Font *self, PyObject *args, PyObject *keywds) {
16605     char *filename;
16606     char *locfilename = NULL;
16607     FontViewBase *fv;
16608     PyObject *flags=NULL, *ttcflags=NULL, *others=NULL;
16609     int iflags = old_sfnt_flags;
16610     int ittcflags = 0;
16611     const char *bitmaptype="", *namelist=NULL;
16612     NameList *rename_to = NULL;
16613     int layer;
16614     char *layer_str=NULL;
16615     struct sflist *head=NULL, *last, *cur;
16616     enum bitmapformat bf = bf_none;
16617 
16618     if ( CheckIfFontClosed(self) )
16619 return(NULL);
16620     fv = self->fv;
16621 
16622     if ( !PyArg_ParseTupleAndKeywords(args, keywds, "sO|sOOsi", (char **)genttc_keywords,
16623 	    &filename, &others, &bitmaptype, &flags, &ttcflags,
16624 	    &namelist, &layer) ) {
16625 	PyErr_Clear();
16626 	if ( !PyArg_ParseTupleAndKeywords(args, keywds, "sO|sOOss", (char **)genttc_keywords,
16627 		&filename, &others, &bitmaptype, &flags, &ttcflags,
16628 		&namelist, &layer_str) )
16629 return( NULL );
16630 	layer = SFFindLayerIndexByName(fv->sf,layer_str);
16631 	if ( layer<0 )
16632 return( NULL );
16633     }
16634     if ( layer<0 || layer>=fv->sf->layer_cnt ) {
16635 	PyErr_Format(PyExc_ValueError, "Layer is out of range" );
16636 return( NULL );
16637     }
16638     if ( flags!=NULL ) {
16639 	iflags = FlagsFromTuple(flags,gen_flags,"generate flag");
16640 	if ( iflags==FLAG_UNKNOWN ) {
16641 return( NULL );
16642 	}
16643 	/* Legacy screw ups mean that opentype & apple bits don't mean what */
16644 	/*  I want them to. Python users should not see that, but fix it up */
16645 	/*  here */
16646 	if ( (iflags&0x80) && (iflags&0x10) )	/* Both */
16647 	    iflags &= ~0x10;
16648 	else if ( (iflags&0x80) && !(iflags&0x10)) /* Just opentype */
16649 	    iflags &= ~0x80;
16650 	else if ( !(iflags&0x80) && (iflags&0x10)) /* Just apple */
16651 	    /* This one's set already */;
16652 	else
16653 	    iflags |= 0x90;
16654     }
16655     if ( ttcflags!=NULL ) {
16656 	ittcflags = FlagsFromTuple(ttcflags,genttc_flags,"generate TTC flag");
16657 	if ( ittcflags==FLAG_UNKNOWN ) {
16658 return( NULL );
16659 	}
16660     }
16661     if ( bitmaptype!=NULL && *bitmaptype!='\0' ) {
16662 	if ( strcasecmp(bitmaptype,"ttf")==0 )
16663 	    bf = bf_ttf;
16664 	else {
16665 	    PyErr_Format(PyExc_TypeError, "Unknown bitmap format");
16666 return( NULL );
16667 	}
16668     }
16669     if ( namelist!=NULL ) {
16670 	rename_to = NameListByName(namelist);
16671 	if ( rename_to==NULL ) {
16672 	    PyErr_Format(PyExc_EnvironmentError, "Unknown namelist");
16673 return( NULL );
16674 	}
16675     }
16676 
16677     head = last = makesflist(self,bf);
16678     if ( others==Py_None )
16679 	/* Silly to have a ttc with just one font, but ok */;
16680     else if ( PyType_IsSubtype(&PyFF_FontType, Py_TYPE(others)) ) {
16681 	PyFF_Font* otherfont = (PyFF_Font*)others;
16682 	if ( CheckIfFontClosed(otherfont) ) {
16683 	    freesflist(head);
16684 return(NULL);
16685 	}
16686 	cur = makesflist(otherfont,bf);
16687 	last->next = cur;
16688 	last = cur;
16689     } else if ( PySequence_Check(others) ) {
16690 	int i, subcnt = PySequence_Size(others);
16691 	for ( i=0; i<subcnt; ++i ) {
16692 	    PyObject *item = PySequence_GetItem(others,i);
16693 	    if ( PyType_IsSubtype(&PyFF_FontType, Py_TYPE(item)) ) {
16694 		PyFF_Font* otherfont = (PyFF_Font*)item;
16695 		if( CheckIfFontClosed(otherfont) ) {
16696 		    freesflist(head);
16697 return(NULL);
16698 		}
16699 		cur = makesflist(otherfont,bf);
16700 		last->next = cur;
16701 		last = cur;
16702 	    } else {
16703 		PyErr_Format(PyExc_TypeError, "Second argument must be either a font or a list of fonts");
16704 return( NULL );
16705 	    }
16706 	}
16707     } else {
16708 	PyErr_Format(PyExc_TypeError, "Second argument must be either a font or a list of fonts");
16709 return( NULL );
16710     }
16711 
16712     locfilename = utf82def_copy(filename);
16713 
16714     if ( !WriteTTC(locfilename,head,ff_ttc,bf,iflags,layer,ittcflags)) {
16715 	PyErr_Format(PyExc_EnvironmentError, "Font generation failed");
16716 	/* Don't return here, will return after memory is freed below */
16717     }
16718     free(locfilename);
16719     freesflist(head);
16720     if ( PyErr_Occurred() )
16721 return( NULL );
16722 Py_RETURN( self );
16723 }
16724 
PyFFFont_GenerateFeature(PyFF_Font * self,PyObject * args)16725 static PyObject *PyFFFont_GenerateFeature(PyFF_Font *self, PyObject *args) {
16726     char *filename;
16727     char *locfilename = NULL;
16728     char *lookup_name = NULL;
16729     FontViewBase *fv;
16730     FILE *out;
16731     OTLookup *otl = NULL;
16732     int err;
16733 
16734     if ( CheckIfFontClosed(self) )
16735 return (NULL);
16736     fv = self->fv;
16737     if ( !PyArg_ParseTuple(args,"s|s",&filename,&lookup_name) )
16738 return( NULL );
16739     locfilename = utf82def_copy(filename);
16740 
16741     if ( lookup_name!=NULL ) {
16742 	otl = SFFindLookup(fv->sf,lookup_name);
16743 	if ( otl == NULL ) {
16744 	    PyErr_Format(PyExc_EnvironmentError, "No lookup named %s", lookup_name );
16745 return( NULL );
16746 	}
16747     }
16748     out = fopen(locfilename,"w");
16749     if ( out==NULL ) {
16750 	PyErr_SetFromErrnoWithFilename(PyExc_IOError,locfilename);
16751 	free(locfilename);
16752 return( NULL );
16753     }
16754     if ( otl!=NULL )
16755 	FeatDumpOneLookup(out,fv->sf,otl);
16756     else
16757 	FeatDumpFontLookups(out,fv->sf);
16758     err = ferror(out);
16759     if ( fclose(out)!=0 || err ) {
16760 	PyErr_Format(PyExc_EnvironmentError, "IO error on file %s", locfilename);
16761 	free(locfilename);
16762 return( NULL );
16763     }
16764     free(locfilename);
16765 Py_RETURN( self );
16766 }
16767 
PyFFFont_MergeKern(PyFF_Font * self,PyObject * args)16768 static PyObject *PyFFFont_MergeKern(PyFF_Font *self, PyObject *args) {
16769     char *filename;
16770     char *locfilename = NULL;
16771     FontViewBase *fv;
16772 
16773     if ( CheckIfFontClosed(self) )
16774 return (NULL);
16775     fv = self->fv;
16776     if ( !PyArg_ParseTuple(args,"s",&filename) )
16777 return( NULL );
16778     locfilename = utf82def_copy(filename);
16779 
16780     if ( !LoadKerningDataFromMetricsFile(fv->sf,locfilename,fv->map)) {
16781 	PyErr_Format(PyExc_EnvironmentError, "No metrics data found");
16782 return( NULL );
16783     }
16784     free(locfilename);
16785 Py_RETURN( self );
16786 }
16787 
PyFFFont_MergeFonts(PyFF_Font * self,PyObject * args)16788 static PyObject *PyFFFont_MergeFonts(PyFF_Font *self, PyObject *args) {
16789     char *filename;
16790     char *locfilename = NULL;
16791     FontViewBase *fv;
16792     SplineFont *sf;
16793     int openflags=0;
16794     int preserveCrossFontKerning = 0;
16795 
16796     if ( CheckIfFontClosed(self) )
16797 return (NULL);
16798     fv = self->fv;
16799     if ( !PyArg_ParseTuple(args,"s|ii",&filename,
16800 	    &preserveCrossFontKerning, &openflags) ) {
16801 	PyFF_Font *other;
16802 	PyErr_Clear();
16803 	if ( !PyArg_ParseTuple(args,"O!|i",&PyFF_FontType,&other,
16804 		&preserveCrossFontKerning) || CheckIfFontClosed(other) )
16805 return( NULL );
16806 	sf = other->fv->sf;
16807     } else {
16808 	locfilename = utf82def_copy(filename);
16809 	sf = LoadSplineFont(locfilename,openflags);
16810 	if ( sf==NULL ) {
16811 	    PyErr_Format(PyExc_EnvironmentError, "No font found in file \"%s\"", locfilename);
16812 	    free(locfilename);
16813 return( NULL );
16814 	}
16815     }
16816     free(locfilename);
16817     if ( sf->fv==NULL )
16818 	EncMapFree(sf->map);
16819     MergeFont(fv,sf,preserveCrossFontKerning);
16820 Py_RETURN( self );
16821 }
16822 
PyFFFont_InterpolateFonts(PyFF_Font * self,PyObject * args)16823 static PyObject *PyFFFont_InterpolateFonts(PyFF_Font *self, PyObject *args) {
16824     char *filename;
16825     char *locfilename = NULL;
16826     FontViewBase *fv, *newfv;
16827     SplineFont *sf;
16828     int openflags=0;
16829     double fraction;
16830 
16831     if ( CheckIfFontClosed(self) )
16832 return (NULL);
16833     fv = self->fv;
16834     if ( !PyArg_ParseTuple(args,"ds|i",&fraction,&filename, &openflags) )
16835 return( NULL );
16836     locfilename = utf82def_copy(filename);
16837     sf = LoadSplineFont(locfilename,openflags);
16838     if ( sf==NULL ) {
16839 	PyErr_Format(PyExc_EnvironmentError, "No font found in file \"%s\"", locfilename);
16840 	free(locfilename);
16841 return( NULL );
16842     }
16843     free(locfilename);
16844     if ( sf->fv==NULL )
16845 	EncMapFree(sf->map);
16846     newfv = SFAdd(InterpolateFont(fv->sf,sf,fraction, fv->map->enc ),false);
16847 return( PyFF_FontForFV_I(newfv));
16848 }
16849 
PyFFFont_CreateMappedChar(PyFF_Font * self,PyObject * args)16850 static PyObject *PyFFFont_CreateMappedChar(PyFF_Font *self, PyObject *args) {
16851     int enc;
16852     char *str;
16853     FontViewBase *fv;
16854     SplineChar *sc;
16855 
16856     if ( CheckIfFontClosed(self) )
16857 return (NULL);
16858     fv = self->fv;
16859     if ( !PyArg_ParseTuple(args,"s", &str ) ) {
16860 	PyErr_Clear();
16861 	if ( !PyArg_ParseTuple(args,"i", &enc ) )
16862 return( NULL );
16863 	if ( enc<0 || enc>fv->map->enccount ) {
16864 	    PyErr_Format(PyExc_ValueError, "Encoding is out of range" );
16865 return( NULL );
16866 	}
16867     } else {
16868 	enc = SFFindSlot(fv->sf, fv->map, -1, str );
16869 	if ( enc==-1 ) {
16870 	    PyErr_Format(PyExc_ValueError, "Glyph name, %s, not in current encoding", str );
16871 return( NULL );
16872 	}
16873     }
16874     sc = SFMakeChar(fv->sf,fv->map,enc);
16875 return( PySC_From_SC_I( sc ));
16876 }
16877 
PyFFFont_CreateUnicodeChar(PyFF_Font * self,PyObject * args)16878 static PyObject *PyFFFont_CreateUnicodeChar(PyFF_Font *self, PyObject *args) {
16879     int uni, enc;
16880     char *name=NULL;
16881     FontViewBase *fv;
16882     SplineChar *sc;
16883 
16884     if ( CheckIfFontClosed(self) )
16885 return (NULL);
16886     fv = self->fv;
16887     if ( !PyArg_ParseTuple(args,"i|s", &uni, &name ) )
16888 return( NULL );
16889     if ( uni<-1 || uni >= (int)unicode4_size ) {
16890 	PyErr_Format(PyExc_ValueError, "Unicode codepoint, %d, out of range, must be either -1 or between 0 and 0x10ffff", uni );
16891 return( NULL );
16892     } else if ( uni==-1 && name==NULL ) {
16893 	PyErr_Format(PyExc_ValueError, "If you do not specify a code point, you must specify a name.");
16894 return( NULL );
16895     }
16896 
16897     enc = SFFindSlot(fv->sf, fv->map, uni, name );
16898     if ( enc!=-1 ) {
16899 	sc = SFMakeChar(fv->sf,fv->map,enc);
16900 	if ( name!=NULL ) {
16901 	    free(sc->name);
16902 	    sc->name = copy(name);
16903 	    GlyphHashFree(fv->sf);
16904 	}
16905     } else {
16906 	sc = SFGetOrMakeChar(fv->sf,uni,name);
16907 	/* does not add to current map. But since we didn't find a slot it's */
16908 	/*  not in the encoding. We could add it in the unencoded area */
16909     }
16910 return( PySC_From_SC_I( sc ));
16911 }
16912 
PyFFFont_CreateInterpolatedGlyph(PyFF_Font * self,PyObject * args)16913 static PyObject *PyFFFont_CreateInterpolatedGlyph(PyFF_Font *self, PyObject *args) {
16914     FontViewBase *fv;
16915     SplineFont *sf;
16916     PyObject *from, *to;
16917     double by;
16918     SplineChar *sc;
16919     int baseenc;
16920 
16921     if ( CheckIfFontClosed(self) )
16922 return (NULL);
16923     fv = self->fv;
16924     sf = fv->sf;
16925     if ( !PyArg_ParseTuple(args,"OOd",&from,&to,&by) )
16926 return( NULL );
16927     if ( !PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(from)) ||
16928          !PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(to))) {
16929 	PyErr_Format(PyExc_TypeError, "Expected glyph objects");
16930 return( NULL );
16931     }
16932     if ( SFGetChar(fv->sf,((PyFF_Glyph *) from)->sc->unicodeenc,((PyFF_Glyph *) from)->sc->name)!=NULL ) {
16933 	PyErr_Format(PyExc_EnvironmentError, "This glyph already exists in the font");
16934 return( NULL );
16935     }
16936 
16937     sc = SplineCharInterpolate(((PyFF_Glyph *) from)->sc,((PyFF_Glyph *) to)->sc,by, sf);
16938     if ( sc==NULL ) {
16939 	PyErr_Format(PyExc_EnvironmentError, "Interpolation failed");
16940 return( NULL );
16941     }
16942 
16943     baseenc = EncFromUni(sc->unicodeenc,fv->map->enc);
16944     if ( baseenc==-1 )
16945 	baseenc = fv->map->enccount+1;
16946 
16947     SFAddGlyphAndEncode(sf,sc,fv->map,baseenc);
16948 return( PySC_From_SC_I( sc ));
16949 }
16950 
PyFFFont_findEncodingSlot(PyFF_Font * self,PyObject * args)16951 static PyObject *PyFFFont_findEncodingSlot(PyFF_Font *self, PyObject *args) {
16952     int uni= -1;
16953     char *name=NULL;
16954     FontViewBase *fv;
16955 
16956     if ( CheckIfFontClosed(self) )
16957 return (NULL);
16958     fv = self->fv;
16959     if ( !PyArg_ParseTuple(args,"s", &name ) ) {
16960 	PyErr_Clear();
16961 	if ( !PyArg_ParseTuple(args,"i", &uni ) )
16962 return( NULL );
16963     }
16964     if ( uni<-1 || uni >= (int)unicode4_size ) {
16965 	PyErr_Format(PyExc_ValueError, "Unicode codepoint, %d, out of range, must be either -1 or between 0 and 0x10ffff", uni );
16966 return( NULL );
16967     }
16968 
16969 return( Py_BuildValue("i",SFFindSlot(fv->sf, fv->map, uni, name )) );
16970 }
16971 
PyFFFont_removeGlyph(PyFF_Font * self,PyObject * args)16972 static PyObject *PyFFFont_removeGlyph(PyFF_Font *self, PyObject *args) {
16973     int uni;
16974     char *name=NULL;
16975     FontViewBase *fv;
16976     SplineChar *sc;
16977 
16978     if ( CheckIfFontClosed(self) )
16979 return (NULL);
16980     fv = self->fv;
16981     if ( PyTuple_Size(args)==1 && PyType_IsSubtype(&PyFF_GlyphType, Py_TYPE(PyTuple_GetItem(args,0))) ) {
16982 	sc = ((PyFF_Glyph *) PyTuple_GetItem(args,0))->sc;
16983 	if ( sc->parent!=fv->sf ) {
16984 	    PyErr_Format(PyExc_ValueError, "This glyph is not in the font");
16985 return( NULL );
16986 	}
16987     } else {
16988 	if ( PyTuple_Size(args)==1 && PyUnicode_Check(PyTuple_GetItem(args,0)) ) {
16989 	    if ( !PyArg_ParseTuple(args,"s", &name ) )
16990 return( NULL );
16991 	    uni = -1;
16992 	} else if ( !PyArg_ParseTuple(args,"i|s", &uni, &name ) )
16993 return( NULL );
16994 	if ( uni<-1 || uni >= (int)unicode4_size ) {
16995 	    PyErr_Format(PyExc_ValueError, "Unicode codepoint, %d, out of range, must be either -1 or between 0 and 0x10ffff", uni );
16996 return( NULL );
16997 	} else if ( uni==-1 && name==NULL ) {
16998 	    PyErr_Format(PyExc_ValueError, "If you do not specify a code point, you must specify a name.");
16999 return( NULL );
17000 	}
17001 	sc = SFGetChar(fv->sf,uni,name);
17002 	if ( sc==NULL ) {
17003 	    PyErr_Format(PyExc_ValueError, "This glyph is not in the font");
17004 return( NULL );
17005 	}
17006     }
17007     SFRemoveGlyph(fv->sf,sc);
17008 Py_RETURN( self );
17009 }
17010 
17011 
17012 /* Print sample flags */
17013 static struct flaglist printflags[] = {
17014     { "fontdisplay", 0 },
17015     { "chars", 1 },
17016     { "waterfall", 2 },
17017     { "fontsample", 3 },
17018     { "fontsampleinfile", 4 },
17019     { "multisize", 2 },
17020     FLAGLIST_EMPTY /* Sentinel */
17021 };
17022 
17023 /* font.printSample(type, [pointsize, [sample, [output-filename]]])     */
17024 
PyFFFont_printSample(PyFF_Font * self,PyObject * args)17025 static PyObject *PyFFFont_printSample(PyFF_Font *self, PyObject *args) {
17026     int type, i, inlinesample = true;
17027     char *typeArg, *sampleArg=NULL, *outputArg=NULL;
17028     int pointsizeArg=INT_MIN;
17029     PyObject *pointsizeTuple=NULL;
17030     PyObject *arg;
17031     int32 *pointsizes=NULL;
17032     char *samplefile=NULL;
17033     unichar_t *sample=NULL;
17034 
17035     if ( CheckIfFontClosed(self) )
17036         return (NULL);
17037 
17038     /* Attempt parse assuming pointsize is a single integer */
17039     if ( !PyArg_ParseTuple(args,"s|iss",&typeArg,&pointsizeArg,&sampleArg,&outputArg) ) {
17040         PyErr_Clear();
17041         /* Attempt parse hoping pointsize is a tuple/list of integers */
17042         if ( !PyArg_ParseTuple(args,"s|Oss",&typeArg,&pointsizeTuple,&sampleArg,&outputArg) ) {
17043             PyErr_Format(PyExc_TypeError,"Expecting 1 to 4 args with type string as first arg");
17044             return( NULL );
17045         }
17046         if ( !PyTuple_Check(pointsizeTuple) && !PyList_Check(pointsizeTuple)) {
17047             PyErr_Format(PyExc_TypeError, "Second arg must be an integer, or a tuple or list of integers" );
17048             return( NULL );
17049         }
17050         if ( PySequence_Size(pointsizeTuple) < 1 ) {
17051             PyErr_Format(PyExc_TypeError, "Second arg must be an integer, or a tuple or list of integers" );
17052             return( NULL );
17053         }
17054     }
17055 
17056     type = FlagsFromString(typeArg,printflags,"print sample type");
17057     if ( type==FLAG_UNKNOWN ) {
17058         return( NULL );
17059     }
17060     if ( type==4 ) {
17061         type=3;
17062         inlinesample = false;
17063     }
17064     if ( pointsizeArg!=INT_MIN ) {
17065         if ( pointsizeArg>0 ) {
17066             pointsizes = calloc(2,sizeof(int32));
17067             pointsizes[0] = pointsizeArg;
17068         }
17069     } else if ( pointsizeTuple!=NULL ) {
17070         int subcnt = PySequence_Size(pointsizeTuple);
17071         pointsizes = malloc((subcnt+1)*sizeof(int32));
17072         for ( i=0; i<subcnt; ++i ) {
17073             arg = PySequence_GetItem(pointsizeTuple,i);
17074             pointsizes[i] = PyLong_AsLong(arg);
17075             Py_DECREF(arg);
17076             if ( PyErr_Occurred()) {
17077                 free(pointsizes);
17078                 return( NULL );
17079             }
17080         }
17081         pointsizes[i] = 0;
17082     }
17083     if ( sampleArg!=NULL ) {
17084         if ( inlinesample ) {
17085             sample = utf82u_copy(sampleArg);
17086             samplefile = NULL;
17087         } else {
17088             samplefile = utf82def_copy(sampleArg);
17089             sample = NULL;
17090         }
17091     }
17092 
17093     ScriptPrint(self->fv,type,pointsizes,samplefile,sample,outputArg);
17094     free(pointsizes);
17095     free(samplefile);
17096     /* ScriptPrint frees sample for us */
17097     Py_RETURN(self);
17098 }
17099 
PyFFFont_randomText(PyFF_Font * self,PyObject * args)17100 static PyObject *PyFFFont_randomText(PyFF_Font *self, PyObject *args) {
17101     FontViewBase *fv;
17102     char *script=NULL, *lang=NULL, *txt;
17103     uint32 stag, ltag=0;
17104     PyObject *ret;
17105 
17106     if ( CheckIfFontClosed(self) )
17107 return (NULL);
17108     fv = self->fv;
17109     if ( !PyArg_ParseTuple(args,"s|s",&script, &lang ))
17110 return( NULL );
17111     stag = StrToTag(script,NULL);
17112     if ( lang!=NULL ) {
17113 	ltag = StrToTag(lang,NULL);
17114 	txt = RandomParaFromScriptLang(stag,ltag,fv->sf,NULL);
17115     } else
17116 	txt = RandomParaFromScript(stag,&ltag,fv->sf);
17117     ret = Py_BuildValue("s",txt);
17118     free(txt);
17119 return( ret );
17120 }
17121 
PyFFFont_clear(PyFF_Font * self,PyObject * UNUSED (args))17122 static PyObject *PyFFFont_clear(PyFF_Font *self, PyObject *UNUSED(args)) {
17123     FontViewBase *fv;
17124     if ( CheckIfFontClosed(self) )
17125 return (NULL);
17126     fv = self->fv;
17127     FVClear(fv);
17128 Py_RETURN(self);
17129 }
17130 
PyFFFont_cut(PyFF_Font * self,PyObject * UNUSED (args))17131 static PyObject *PyFFFont_cut(PyFF_Font *self, PyObject *UNUSED(args)) {
17132     FontViewBase *fv;
17133     if ( CheckIfFontClosed(self) )
17134 return (NULL);
17135     fv = self->fv;
17136     FVCopy(fv,ct_fullcopy);
17137     FVClear(fv);
17138 Py_RETURN(self);
17139 }
17140 
PyFFFont_copy(PyFF_Font * self,PyObject * UNUSED (args))17141 static PyObject *PyFFFont_copy(PyFF_Font *self, PyObject *UNUSED(args)) {
17142     FontViewBase *fv;
17143     if ( CheckIfFontClosed(self) )
17144 return (NULL);
17145     fv = self->fv;
17146     FVCopy(fv,ct_fullcopy);
17147 Py_RETURN(self);
17148 }
17149 
PyFFFont_copyReference(PyFF_Font * self,PyObject * UNUSED (args))17150 static PyObject *PyFFFont_copyReference(PyFF_Font *self, PyObject *UNUSED(args)) {
17151     FontViewBase *fv;
17152     if ( CheckIfFontClosed(self) )
17153 return (NULL);
17154     fv = self->fv;
17155     FVCopy(fv,ct_reference);
17156 Py_RETURN(self);
17157 }
17158 
PyFFFont_paste(PyFF_Font * self,PyObject * UNUSED (args))17159 static PyObject *PyFFFont_paste(PyFF_Font *self, PyObject *UNUSED(args)) {
17160     FontViewBase *fv;
17161     if ( CheckIfFontClosed(self) )
17162 return (NULL);
17163     fv = self->fv;
17164     PasteIntoFV(fv,false,NULL);
17165 Py_RETURN(self);
17166 }
17167 
PyFFFont_pasteInto(PyFF_Font * self,PyObject * UNUSED (args))17168 static PyObject *PyFFFont_pasteInto(PyFF_Font *self, PyObject *UNUSED(args)) {
17169     FontViewBase *fv;
17170     if ( CheckIfFontClosed(self) )
17171 return (NULL);
17172     fv = self->fv;
17173     PasteIntoFV(fv,true,NULL);
17174 Py_RETURN(self);
17175 }
17176 
PyFFFont_unlinkReferences(PyFF_Font * self,PyObject * UNUSED (args))17177 static PyObject *PyFFFont_unlinkReferences(PyFF_Font *self, PyObject *UNUSED(args)) {
17178     FontViewBase *fv;
17179     if ( CheckIfFontClosed(self) )
17180 return (NULL);
17181     fv = self->fv;
17182     FVUnlinkRef(fv);
17183 Py_RETURN(self);
17184 }
17185 
17186 
PyFFFont_Build(PyFF_Font * self,PyObject * UNUSED (args))17187 static PyObject *PyFFFont_Build(PyFF_Font *self, PyObject *UNUSED(args)) {
17188     FontViewBase *fv;
17189 
17190     if ( CheckIfFontClosed(self) )
17191 return (NULL);
17192     fv = self->fv;
17193     FVBuildAccent(fv,false);
17194 
17195 Py_RETURN( self );
17196 }
17197 
PyFFFont_canonicalContours(PyFF_Font * self,PyObject * UNUSED (args))17198 static PyObject *PyFFFont_canonicalContours(PyFF_Font *self, PyObject *UNUSED(args)) {
17199     FontViewBase *fv;
17200     EncMap *map;
17201     SplineFont *sf;
17202     int i,gid;
17203 
17204     if ( CheckIfFontClosed(self) )
17205 return (NULL);
17206     fv = self->fv;
17207     sf = fv->sf;
17208     map = fv->map;
17209     for ( i=0; i<map->enccount; ++i ) if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL && fv->selected[i] )
17210 	CanonicalContours(sf->glyphs[gid],fv->active_layer);
17211 
17212 Py_RETURN( self );
17213 }
17214 
PyFFFont_canonicalStart(PyFF_Font * self,PyObject * UNUSED (args))17215 static PyObject *PyFFFont_canonicalStart(PyFF_Font *self, PyObject *UNUSED(args)) {
17216     FontViewBase *fv;
17217     EncMap *map;
17218     SplineFont *sf;
17219     int i,gid;
17220 
17221     if ( CheckIfFontClosed(self) )
17222 return (NULL);
17223     fv = self->fv;
17224     sf = fv->sf;
17225     map = fv->map;
17226     for ( i=0; i<map->enccount; ++i ) if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL && fv->selected[i] )
17227 	SPLsStartToLeftmost(sf->glyphs[gid],fv->active_layer);
17228 
17229 Py_RETURN( self );
17230 }
17231 
PyFFFont_changeWeight(PyFF_Font * self,PyObject * args)17232 static PyObject *PyFFFont_changeWeight(PyFF_Font *self, PyObject *args) {
17233     FontViewBase *fv;
17234     enum embolden_type type;
17235     struct lcg_zones zones;
17236 
17237     if ( CheckIfFontClosed(self) )
17238 return (NULL);
17239     fv = self->fv;
17240     type = CW_ParseArgs(fv->sf,&zones,args);
17241     if ( type == embolden_error )
17242 return( NULL );
17243     FVEmbolden(fv,type,&zones);
17244 
17245 Py_RETURN( self );
17246 }
17247 
PyFFFont_condenseExtend(PyFF_Font * self,PyObject * args)17248 static PyObject *PyFFFont_condenseExtend(PyFF_Font *self, PyObject *args) {
17249     FontViewBase *fv;
17250     struct counterinfo ci;
17251 
17252     if ( CheckIfFontClosed(self) )
17253 return (NULL);
17254     fv = self->fv;
17255     memset(&ci,0,sizeof(ci));
17256     ci.sb_factor = ci.sb_add = -10000;
17257     ci.correct_italic = true;
17258 
17259     if ( !PyArg_ParseTuple(args,"dd|ddi",&ci.c_factor, &ci.c_add, &ci.sb_factor, &ci.sb_add,
17260 	    &ci.correct_italic ))
17261 return( NULL );
17262     ci.c_factor *= 100;			/* UI uses a percent */
17263     if ( ci.sb_factor == -10000 )
17264 	ci.sb_factor = ci.c_factor;
17265     if ( ci.sb_add == -10000 )
17266 	ci.sb_add = ci.c_add;
17267     CI_Init(&ci,fv->sf);
17268     FVCondenseExtend(fv,&ci);
17269 
17270 Py_RETURN( self );
17271 }
17272 
17273 static const char *italicize_keywords[] = {
17274     "italic_angle", "ia",
17275     "lc_condense", "lc",
17276     "uc_condense", "uc",
17277     "symbol_condense", "symbol",
17278     "deserif_flat", "deserif_slant", "deserif_pen",
17279     "baseline_serifs",
17280     "xheight_serifs",
17281     "ascent_serifs",
17282     "descent_serifs",
17283     "diagonal_serifs",
17284     "a",
17285     "f",
17286     "u0444",
17287     "u0438",
17288     "u043f",
17289     "u0442",
17290     "u0448",
17291     "u0452",
17292     "u045f",
17293     NULL
17294 };
17295 
17296 static ItalicInfo default_ii = {
17297     -13,                    /* Italic angle (in degrees) */
17298     .95,                    /* xheight percent */
17299 
17300     /* horizontal squash, lsb, stemsize, countersize, rsb */
17301     { .91, .89, .90, .91 }, /* For lower case */
17302     { .91, .93, .93, .91 }, /* For upper case */
17303     { .91, .93, .93, .91 }, /* For things which are neither upper nor lower case */
17304     srf_flat,               /* Secondary serifs (initial, medial on "m", descender on "p", "q" */
17305     true,                   /* Transform bottom serifs */
17306     true,                   /* Transform serifs at x-height */
17307     false,                  /* Transform serifs on ascenders */
17308     true,                   /* Transform serifs on diagonal stems at baseline and x-height */
17309 
17310     true,                   /* Change the shape of an "a" to look like a "d" without ascender */
17311     false,                  /* Change the shape of "f" so it descends below baseline (straight down no flag at end) */
17312     true,                   /* Change the shape of "f" so the bottom looks like the top */
17313     true,                   /* Remove serifs from the bottom of descenders */
17314 
17315     true,                   /* Make the cyrillic "phi" glyph have a top like an "f" */
17316     true,                   /* Make the cyrillic "i" glyph look like a latin "u" */
17317     true,                   /* Make the cyrillic "pi" glyph look like a latin "n" */
17318     true,                   /* Make the cyrillic "te" glyph look like a latin "m" */
17319     true,                   /* Make the cyrillic "sha" glyph look like a latin "m" rotated 180 */
17320     true,                   /* Make the cyrillic "dje" glyph look like a latin smallcaps T (not implemented) */
17321     true,                   /* Make the cyrillic "dzhe" glyph look like a latin "u" (same glyph used for cyrillic "i") */
17322 
17323     ITALICINFO_REMAINDER
17324 };
17325 
SquashParse(struct hsquash * squash,PyObject * po)17326 static int SquashParse(struct hsquash *squash,PyObject *po) {
17327     if ( po==NULL )
17328 return( true );
17329 
17330     if ( PyFloat_Check(po)) {
17331 	squash->lsb_percent = squash->stem_percent = squash->counter_percent = squash->rsb_percent =
17332 		PyFloat_AsDouble(po);
17333     } else if ( !PyArg_ParseTuple(po,"dddd",
17334 	    squash->lsb_percent, squash->stem_percent,
17335 	    squash->counter_percent, squash->rsb_percent ))
17336 return( false );
17337 
17338 return( true );
17339 }
17340 
Parse_ItalicArgs(ItalicInfo * ii,PyObject * args,PyObject * keywds)17341 static int Parse_ItalicArgs(ItalicInfo *ii, PyObject *args, PyObject *keywds) {
17342     PyObject *lc=NULL, *uc=NULL, *symbols=NULL;
17343     int deserif_flat=false, deserif_slant=false, deserif_pen=false;
17344     int bottom_serif=default_ii.transform_bottom_serifs;
17345     int xh_serif = default_ii.transform_top_xh_serifs;
17346     int as_serif = default_ii.transform_top_as_serifs;
17347     int dg_serif = default_ii.transform_diagon_serifs;
17348     int ds_serif = default_ii.pq_deserif;
17349     int a = default_ii.a_from_d;
17350     int f = default_ii.f_long_tail ? 2 : default_ii.f_rotate_top ? 1 : 0;
17351     int u0444 = default_ii.cyrl_phi;
17352     int u0438 = default_ii.cyrl_i;
17353     int u043f = default_ii.cyrl_pi;
17354     int u0442 = default_ii.cyrl_te;
17355     int u0448 = default_ii.cyrl_sha;
17356     int u0452 = default_ii.cyrl_dje;
17357     int u045f = default_ii.cyrl_dzhe;
17358 
17359     *ii = default_ii;
17360     if ( !PyArg_ParseTupleAndKeywords(args,keywds,"|ddOOOOOOiiiiiiiiiiiiiiiii",(char **)italicize_keywords,
17361 	    &ii->italic_angle, &ii->italic_angle,
17362 	    &lc, &lc,
17363 	    &uc, &uc,
17364 	    &symbols, &symbols,
17365 	    &deserif_flat, &deserif_slant, &deserif_pen,
17366 	    &bottom_serif, &xh_serif, &as_serif, &dg_serif, &ds_serif,
17367 	    &a, &f,
17368 	    &u0444, &u0438, &u043f, &u0442, &u0448, &u0452, &u045f ))
17369 return( false );
17370     if ( !SquashParse(&ii->lc,lc) || !SquashParse(&ii->uc,uc) || !SquashParse(&ii->neither,symbols))
17371 return( false );
17372     if ( deserif_flat )
17373 	ii->secondary_serif = srf_flat;
17374     else if ( deserif_slant )
17375 	ii->secondary_serif = srf_simpleslant;
17376     else if ( deserif_pen )
17377 	ii->secondary_serif = srf_complexslant;
17378     ii->transform_bottom_serifs = bottom_serif;
17379     ii->transform_top_xh_serifs = xh_serif;
17380     ii->transform_top_as_serifs = as_serif;
17381     ii->transform_diagon_serifs = dg_serif;
17382     ii->pq_deserif = ds_serif;
17383 
17384     ii->f_long_tail = ii->f_rotate_top = false;
17385     if ( f==2 )
17386 	ii->f_long_tail = true;
17387     else if ( f==1 )
17388 	ii->f_rotate_top = true;
17389     ii->a_from_d = a;
17390 
17391     ii->cyrl_phi  = u0444;
17392     ii->cyrl_i    = u0438;
17393     ii->cyrl_pi   = u043f;
17394     ii->cyrl_te   = u0442;
17395     ii->cyrl_sha  = u0448;
17396     ii->cyrl_dje  = u0452;
17397     ii->cyrl_dzhe = u045f;
17398 return( true );
17399 }
17400 
PyFFFont_italicize(PyFF_Font * self,PyObject * args,PyObject * keywds)17401 static PyObject *PyFFFont_italicize(PyFF_Font *self, PyObject *args, PyObject *keywds) {
17402     FontViewBase *fv;
17403     ItalicInfo ii;
17404 
17405     if ( CheckIfFontClosed(self) )
17406 return (NULL);
17407     fv = self->fv;
17408     if ( !Parse_ItalicArgs(&ii,args,keywds))
17409 return( NULL );
17410     MakeItalic(fv,NULL,&ii);
17411 
17412 Py_RETURN( self );
17413 }
17414 
17415 static const char *smallcaps_keywords[] = { "scheight", "capheight", "lcstem", "ucstem",
17416 	"symbols", "letter_extension", "symbol_extension", "stem_height_factor",
17417 	"hscale", "vscale", NULL };
17418 
PyFFFont_addSmallCaps(PyFF_Font * self,PyObject * args,PyObject * keywds)17419 static PyObject *PyFFFont_addSmallCaps(PyFF_Font *self, PyObject *args, PyObject *keywds) {
17420     FontViewBase *fv;
17421     struct smallcaps small;
17422     struct genericchange genchange;
17423     double lc_width=0, uc_width=0, vstem_factor=0, hscale=0, vscale=0, scheight=0, capheight=0;
17424     int dosymbols=0;
17425 
17426     if ( CheckIfFontClosed(self) )
17427 return (NULL);
17428     fv = self->fv;
17429     memset(&genchange,0,sizeof(genchange));
17430     SmallCapsFindConstants(&small,fv->sf,fv->active_layer);
17431     genchange.small = &small;
17432     genchange.gc = gc_smallcaps;
17433     genchange.extension_for_letters = "sc";
17434     genchange.extension_for_symbols = "taboldstyle";
17435     if ( !PyArg_ParseTupleAndKeywords(args,keywds,"|ddddissddd",(char **)smallcaps_keywords,
17436 	    &scheight,&capheight,&lc_width,&uc_width,
17437 	    &dosymbols, &genchange.extension_for_letters,
17438 	    &genchange.extension_for_symbols,
17439 	    &vstem_factor,
17440 	    &hscale, &vscale))
17441 return( NULL );
17442     if ( lc_width!=0 || uc_width!=0 ) {
17443 	if ( lc_width!=0 )
17444 	    small.lc_stem_width = lc_width;
17445 	if ( uc_width!=0 )
17446 	    small.uc_stem_width = uc_width;
17447 	genchange.stem_width_scale = genchange.stem_height_scale =
17448 		small.lc_stem_width / small.uc_stem_width;
17449     }
17450     genchange.do_smallcap_symbols = dosymbols;
17451     if ( vstem_factor!=0 )
17452 	genchange.stem_height_scale = vstem_factor;
17453     if ( scheight>0 || capheight>0 ) {
17454 	if ( scheight>0 )
17455 	    small.scheight = scheight;
17456 	if ( capheight>0 )
17457 	    small.capheight = capheight;
17458     }
17459     if ( small.capheight>0 )
17460 	genchange.v_scale = genchange.hcounter_scale = small.scheight/small.capheight;
17461     if ( hscale>0 )
17462 	genchange.hcounter_scale = hscale;
17463     genchange.lsb_scale = genchange.rsb_scale = genchange.hcounter_scale;
17464     if ( vscale>0 )
17465 	genchange.v_scale = vscale;
17466 
17467     FVAddSmallCaps(fv,&genchange);
17468 
17469 Py_RETURN( self );
17470 }
17471 
17472 static const char *genchange_keywords[] = {
17473 /* Stem info */
17474 	"stemType",
17475 	"thickThreshold",
17476 	"stemScale", "stemAdd",
17477 	"stemHeightScale", "thinStemScale",
17478 	"stemHeightAdd", "thinStemAdd",
17479 	"stemWidthScale", "thickStemScale",
17480 	"stemWidthAdd", "thickStemAdd",
17481 	"processDiagonalStems",
17482 /* Horizontal counter info */
17483 	"hCounterType",
17484 	"hCounterScale", "hCounterAdd",
17485 	"lsbScale", "lsbAdd",
17486 	"rsbScale", "rsbAdd",
17487 /* Vertical counter info */
17488 	"vCounterType",
17489 	"vCounterScale", "vCounterAdd",
17490 	"vScale",
17491 	"vMap",
17492 	NULL};
17493 
PyFFParse_genericGlyphChange(PyObject * args,PyObject * keywds,struct genericchange * genchange)17494 static bool PyFFParse_genericGlyphChange(PyObject *args, PyObject *keywds,
17495                                          struct genericchange *genchange) {
17496     const char *stemtype = "uniform", *hcountertype="uniform";
17497     const char *vCounterType="mapped";
17498     double thickthreshold=0,
17499 	stemscale=0, stemadd=0,
17500 	stemheightscale=0, thinstemscale=0,
17501 	stemheightadd=0, thinstemadd=0,
17502 	stemwidthscale=0, thickstemscale=0,
17503 	stemwidthadd=0, thickstemadd=0;
17504     int processdiagonalstems=1;
17505     double counterScale=0, counterAdd=0,
17506 	lsbScale=0, lsbAdd=0,
17507 	rsbScale=0, rsbAdd=0;
17508     double vCounterScale=0, vCounterAdd=0,
17509 	vScale=1.0;
17510     PyObject *vMap=NULL;
17511 
17512     memset(genchange,0,sizeof(struct genericchange));
17513 
17514     genchange->gc = gc_generic;
17515 
17516     if ( !PyArg_ParseTupleAndKeywords(args,keywds,"|sdddddddddddisddddddsdddO",(char **)genchange_keywords,
17517 /* Stem info */
17518 	    &stemtype,
17519 	    &thickthreshold,
17520 	    &stemscale, &stemadd,
17521 	    &stemheightscale,&thinstemscale,
17522 	    &stemheightadd, &thinstemadd,
17523 	    &stemwidthscale, &thickstemscale,
17524 	    &stemwidthadd, &thickstemadd,
17525 	    &processdiagonalstems,
17526 /* Horizontal counter info */
17527 	    &hcountertype,
17528 	    &counterScale, &counterAdd,
17529 	    &lsbScale, &lsbAdd,
17530 	    &rsbScale, &rsbAdd,
17531 /* Vertical counter info */
17532 	    &vCounterType,
17533 	    &vCounterScale, vCounterAdd,
17534 	    &vScale,
17535 	    &vMap
17536 	    ) )
17537 	return false;
17538 
17539 /* Stem info */
17540     if ( strcasecmp(stemtype,"uniform")==0 ) {
17541 	if ( stemscale<=0 ) {
17542 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for stemScale: %g.", stemscale );
17543 	    return false;
17544 	}
17545 	genchange->stem_width_scale = genchange->stem_height_scale = stemscale;
17546 	genchange->stem_width_add   = genchange->stem_height_add = stemadd;
17547     } else if ( strcasecmp( stemtype, "thickThin" )==0 ) {
17548 	if ( thinstemscale<=0 ) {
17549 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for thinStemScale: %g.", thinstemscale );
17550 	    return false;
17551 	}
17552 	if ( thickstemscale<=0 ) {
17553 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for thickStemScale: %g.", thickstemscale );
17554 	    return false;
17555 	}
17556 	if ( thickthreshold<=0 ) {
17557 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for thickThreshold: %g.", thickthreshold );
17558 	    return false;
17559 	}
17560 	genchange->stem_width_scale  = thickstemscale;
17561 	genchange->stem_width_add    = thickstemadd;
17562 	genchange->stem_height_scale = thinstemscale;
17563 	genchange->stem_height_add   = thinstemadd;
17564 	genchange->stem_threshold    = thickthreshold;
17565     } else if ( strcasecmp( stemtype, "horizontalVertical" )==0 ) {
17566 	if ( stemheightscale<=0 ) {
17567 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for stemHeightScale: %g.", stemheightscale );
17568 	    return false;
17569 	}
17570 	if ( stemwidthscale<=0 ) {
17571 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for stemWidthScale: %g.", stemwidthscale );
17572 	    return false;
17573 	}
17574 	genchange->stem_width_scale  = stemwidthscale;
17575 	genchange->stem_width_add    = stemwidthadd;
17576 	genchange->stem_height_scale = stemheightscale;
17577 	genchange->stem_height_add   = stemheightadd;
17578     } else {
17579 	PyErr_Format(PyExc_TypeError, "Unexpected value for stemType: %s\n (Try: 'uniform', 'thickThin', or 'horizontalVertical')", stemtype );
17580 	return false;
17581     }
17582     genchange->dstem_control         = processdiagonalstems;
17583     if ( genchange->stem_height_add!=genchange->stem_width_add ) {
17584 	if (( genchange->stem_height_add==0 && genchange->stem_width_add!=0 ) ||
17585 		( genchange->stem_height_add!=0 && genchange->stem_width_add==0 )) {
17586 	    PyErr_SetString(PyExc_TypeError, _("The horizontal and vertical stem add amounts must either both be zero, or neither may be 0"));
17587 	    return false;
17588 	}
17589 	/* if width_add has a different sign than height_add that's also */
17590 	/*  a problem, but this test will catch that too */
17591 	if (( genchange->stem_height_add/genchange->stem_width_add>4 ) ||
17592 		( genchange->stem_height_add/genchange->stem_width_add<.25 )) {
17593 	     PyErr_SetString(PyExc_TypeError, _("The horizontal and vertical stem add amounts may not differ by more than a factor of 4"));
17594 	     return false;
17595 	}
17596     }
17597 
17598     /* Horizontal counter info */
17599     if ( strcasecmp(hcountertype,"uniform")==0 ) {
17600 	if ( counterScale<=0 ) {
17601 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for hCounterScale: %g.", counterScale );
17602 	    return false;
17603 	}
17604 	genchange->hcounter_scale = genchange->lsb_scale = genchange->rsb_scale = counterScale;
17605 	genchange->hcounter_add   = genchange->lsb_add   = genchange->rsb_add   = counterAdd;
17606     } else if ( strcasecmp(hcountertype,"nonUniform")==0 ) {
17607 	if ( counterScale<=0 ) {
17608 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for hCounterScale: %g.", counterScale );
17609 	    return false;
17610 	}
17611 	if ( lsbScale<=0 ) {
17612 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for lsbScale: %g.", lsbScale );
17613 	    return false;
17614 	}
17615 	if ( rsbScale<=0 ) {
17616 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for rsbScale: %g.", rsbScale );
17617 	    return false;
17618 	}
17619 	genchange->hcounter_scale = counterScale;
17620 	genchange->lsb_scale      = lsbScale;
17621 	genchange->rsb_scale      = rsbScale;
17622 	genchange->hcounter_add   = counterAdd;
17623 	genchange->lsb_add        = lsbAdd;
17624 	genchange->rsb_add        = rsbAdd;
17625     } else if ( strcasecmp(hcountertype,"center")==0 ) {
17626 	genchange->center_in_hor_advance = 1;
17627     } else if ( strcasecmp(hcountertype,"retainScale")==0 ) {
17628 	genchange->center_in_hor_advance = 2;
17629     } else {
17630 	PyErr_Format(PyExc_TypeError, "Unexpected value for hCounterType: %s\n (Try: 'uniform', 'nonUniform', 'retain', or 'scale')", hcountertype );
17631 	return false;
17632     }
17633 
17634     /* Vertical counter info */
17635     if ( strcasecmp(vCounterType,"scaled")==0 ) {
17636 	if ( vCounterScale<=0 ) {
17637 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for vCounterScale: %g.", vCounterScale );
17638 	    return false;
17639 	}
17640 	genchange->vcounter_scale = vCounterScale;
17641 	genchange->vcounter_add   = vCounterAdd;
17642 	genchange->use_vert_mapping = false;
17643     } else if ( strcasecmp(vCounterType,"mapped")==0 ) {
17644 	int cnt,i;
17645 	genchange->use_vert_mapping = true;
17646 	if ( vScale<=0 ) {
17647 	    PyErr_Format(PyExc_TypeError, "Unexpected (or unspecified) value for vScale: %g.", vScale );
17648 	    return false;
17649 	}
17650 	genchange->v_scale = vScale;
17651 	if ( vMap==NULL || !PySequence_Check(vMap) || PyUnicode_Check(vMap)) {
17652 	    PyErr_Format(PyExc_TypeError, "vMap should be a tuple (or some other sequence type)." );
17653 	    return false;
17654 	}
17655 	cnt = PySequence_Size(vMap);
17656 	genchange->m.cnt = cnt;
17657 	genchange->m.maps = malloc(cnt*sizeof(struct position_maps));
17658 	for ( i=0; i<cnt; ++i ) {
17659 	    PyObject *subTuple = PySequence_GetItem(vMap,i);
17660 	    if ( subTuple==NULL || !PySequence_Check(subTuple) || PyUnicode_Check(subTuple) || PySequence_Size(subTuple)!=3 ) {
17661 		PyErr_Format(PyExc_TypeError, "vMap should be a tuple of 3-tuples." );
17662 		free(genchange->m.maps);
17663 		return false;
17664 	    }
17665 	    if ( !PyArg_ParseTuple(subTuple,"ddd",
17666 		    &genchange->m.maps[i].current,
17667 		    &genchange->m.maps[i].desired,
17668 		    &genchange->m.maps[i].cur_width) ) {
17669 		free(genchange->m.maps);
17670 		return false;
17671 	    }
17672 	}
17673     } else {
17674 	PyErr_Format(PyExc_TypeError, "Unexpected value for vCounterType: %s\n (Try: 'scaled', or 'mapped')", vCounterType );
17675 	return false;
17676     }
17677     return true;
17678 }
17679 
PyFFFont_genericGlyphChange(PyFF_Font * self,PyObject * args,PyObject * keywds)17680 static PyObject *PyFFFont_genericGlyphChange(PyFF_Font *self, PyObject *args,
17681                                              PyObject *keywds) {
17682     FontViewBase *fv;
17683     struct genericchange genchange;
17684     struct smallcaps small;
17685 
17686     if ( CheckIfFontClosed(self) )
17687 	return NULL;
17688 
17689     fv = self->fv;
17690 
17691     if ( !PyFFParse_genericGlyphChange(args, keywds, &genchange) )
17692 	return NULL;
17693 
17694     SmallCapsFindConstants(&small,fv->sf,fv->active_layer);
17695     genchange.small = &small;
17696 
17697     FVGenericChange( fv, &genchange );
17698     free(genchange.m.maps);
17699 
17700     Py_RETURN( self );
17701 }
17702 
PyFFFont_autoHint(PyFF_Font * self,PyObject * UNUSED (args))17703 static PyObject *PyFFFont_autoHint(PyFF_Font *self, PyObject *UNUSED(args)) {
17704     FontViewBase *fv;
17705 
17706     if ( CheckIfFontClosed(self) )
17707 return (NULL);
17708     fv = self->fv;
17709     FVAutoHint(fv);
17710 Py_RETURN( self );
17711 }
17712 
PyFFFont_autoInstr(PyFF_Font * self,PyObject * UNUSED (args))17713 static PyObject *PyFFFont_autoInstr(PyFF_Font *self, PyObject *UNUSED(args)) {
17714     FontViewBase *fv;
17715 
17716     if ( CheckIfFontClosed(self) )
17717 return (NULL);
17718     fv = self->fv;
17719     FVAutoInstr(fv);
17720 Py_RETURN( self );
17721 }
17722 
17723 static const char *autowidth_keywords[] = { "separation", "minBearing", "maxBearing",
17724 	"height", "loopCnt", NULL };
17725 
PyFFFont_autoWidth(PyFF_Font * self,PyObject * args,PyObject * keywds)17726 static PyObject *PyFFFont_autoWidth(PyFF_Font *self, PyObject *args, PyObject *keywds) {
17727     FontViewBase *fv;
17728     int space, min=10, max=-1, height=0, loop=1;
17729 
17730     if ( CheckIfFontClosed(self) )
17731 return (NULL);
17732     fv = self->fv;
17733     if ( !PyArg_ParseTupleAndKeywords(args,keywds,"i|iiii",(char **)autowidth_keywords,
17734 	    &space,&min,&max,&height,&loop))
17735 return( NULL );
17736     AutoWidth2(fv,space,min,max, height, loop);
17737 Py_RETURN( self );
17738 }
17739 
PyFFFont_autoTrace(PyFF_Font * self,PyObject * UNUSED (args))17740 static PyObject *PyFFFont_autoTrace(PyFF_Font *self, PyObject *UNUSED(args)) {
17741     FontViewBase *fv;
17742 
17743     if ( CheckIfFontClosed(self) )
17744 return (NULL);
17745     fv = self->fv;
17746     FVAutoTrace(fv,false);
17747 Py_RETURN( self );
17748 }
17749 
PyFFFont_Transform(PyFF_Font * self,PyObject * args)17750 static PyObject *PyFFFont_Transform(PyFF_Font *self, PyObject *args) {
17751     FontViewBase *fv;
17752     int i;
17753     double m[6];
17754     real t[6];
17755     BVTFunc bvts[1];
17756 
17757     if ( CheckIfFontClosed(self) )
17758 return (NULL);
17759     fv = self->fv;
17760     if ( !PyArg_ParseTuple(args,"(dddddd)",&m[0], &m[1], &m[2], &m[3], &m[4], &m[5] ) )
17761 return( NULL );
17762     for ( i=0; i<6; ++i )
17763 	t[i] = m[i];
17764     bvts[0].func = bvt_none;
17765     FVTransFunc(fv,t,0,bvts,true);
17766 Py_RETURN( self );
17767 }
17768 
PyFFFont_NLTransform(PyFF_Font * self,PyObject * args)17769 static PyObject *PyFFFont_NLTransform(PyFF_Font *self, PyObject *args) {
17770     FontViewBase *fv;
17771     char *xexpr, *yexpr;
17772 
17773     if ( CheckIfFontClosed(self) )
17774 return (NULL);
17775     fv = self->fv;
17776     if ( !PyArg_ParseTuple(args,"ss", &xexpr, &yexpr ) )
17777 return( NULL );
17778     if ( !SFNLTrans(fv,xexpr,yexpr) ) {
17779 	PyErr_Format(PyExc_TypeError, "Unparseable expression.");
17780 return( NULL );
17781     }
17782 Py_RETURN( self );
17783 }
17784 
PyFFFont_Simplify(PyFF_Font * self,PyObject * args)17785 static PyObject *PyFFFont_Simplify(PyFF_Font *self, PyObject *args) {
17786     static struct simplifyinfo smpl = { sf_normal, 0.75, 0.2, 10, 0, 0, 0 };
17787     FontViewBase *fv;
17788 
17789     if ( CheckIfFontClosed(self) )
17790 return (NULL);
17791     fv = self->fv;
17792     smpl.err = (fv->sf->ascent+fv->sf->descent)/1000.;
17793     smpl.linefixup = (fv->sf->ascent+fv->sf->descent)/500.;
17794     smpl.linelenmax = (fv->sf->ascent+fv->sf->descent)/100.;
17795 
17796     if ( PySequence_Size(args)>=1 )
17797 	smpl.err = PyFloat_AsDouble(PySequence_GetItem(args,0));
17798     if ( !PyErr_Occurred() && PySequence_Size(args)>=2 )
17799 	smpl.flags = FlagsFromTuple( PySequence_GetItem(args,1),simplifyflags,"simplify flag");
17800     if ( !PyErr_Occurred() && PySequence_Size(args)>=3 )
17801 	smpl.tan_bounds = PyFloat_AsDouble( PySequence_GetItem(args,2));
17802     if ( !PyErr_Occurred() && PySequence_Size(args)>=4 )
17803 	smpl.linefixup = PyFloat_AsDouble( PySequence_GetItem(args,3));
17804     if ( !PyErr_Occurred() && PySequence_Size(args)>=5 )
17805 	smpl.linelenmax = PyFloat_AsDouble( PySequence_GetItem(args,4));
17806     if ( PyErr_Occurred() )
17807 return( NULL );
17808     _FVSimplify(self->fv,&smpl);
17809 Py_RETURN( self );
17810 }
17811 
PyFFFont_Round(PyFF_Font * self,PyObject * args)17812 static PyObject *PyFFFont_Round(PyFF_Font *self, PyObject *args) {
17813     double factor=1;
17814     FontViewBase *fv;
17815     SplineFont *sf;
17816     EncMap *map;
17817     int i, gid;
17818 
17819     if ( CheckIfFontClosed(self) )
17820 return (NULL);
17821     fv = self->fv;
17822     sf = fv->sf;
17823     map = fv->map;
17824     if ( !PyArg_ParseTuple(args,"|d",&factor ) )
17825 return( NULL );
17826     for ( i=0; i<map->enccount; ++i ) if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL && fv->selected[i] ) {
17827 	SplineChar *sc = sf->glyphs[gid];
17828 	SCRound2Int( sc,fv->active_layer,factor);
17829     }
17830 Py_RETURN( self );
17831 }
17832 
PyFFFont_Cluster(PyFF_Font * self,PyObject * args)17833 static PyObject *PyFFFont_Cluster(PyFF_Font *self, PyObject *args) {
17834     double within = .1, max = .5;
17835     int i, gid;
17836     FontViewBase *fv;
17837     SplineFont *sf;
17838     EncMap *map;
17839 
17840     if ( CheckIfFontClosed(self) )
17841 return (NULL);
17842     fv = self->fv;
17843     sf = fv->sf;
17844     map = fv->map;
17845     if ( !PyArg_ParseTuple(args,"|dd", &within, &max ) )
17846 return( NULL );
17847 
17848     for ( i=0; i<map->enccount; ++i ) if ( (gid=map->map[i])!=-1 && sf->glyphs[gid]!=NULL && fv->selected[i] ) {
17849 	SplineChar *sc = sf->glyphs[gid];
17850 	SCRoundToCluster( sc,ly_all,false,within,max);
17851     }
17852 Py_RETURN( self );
17853 }
17854 
PyFFFont_AddExtrema(PyFF_Font * self,PyObject * UNUSED (args))17855 static PyObject *PyFFFont_AddExtrema(PyFF_Font *self, PyObject *UNUSED(args)) {
17856     FontViewBase *fv;
17857 
17858     if ( CheckIfFontClosed(self) )
17859 return (NULL);
17860     fv = self->fv;
17861     FVAddExtrema(fv, false);
17862 Py_RETURN( self );
17863 }
17864 
PyFFFont_Stroke(PyFF_Font * self,PyObject * args,PyObject * keywds)17865 static PyObject *PyFFFont_Stroke(PyFF_Font *self, PyObject *args, PyObject *keywds) {
17866     StrokeInfo si;
17867 
17868     if ( CheckIfFontClosed(self) )
17869 	return (NULL);
17870     if ( Stroke_Parse(&si, args, keywds)==-1 )
17871 	return( NULL );
17872 
17873     FVStrokeItScript(self->fv, &si,false);
17874     SplinePointListsFree(si.nib); si.nib = NULL;
17875 Py_RETURN( self );
17876 }
17877 
PyFFFont_correctDirection(PyFF_Font * self,PyObject * UNUSED (args))17878 static PyObject *PyFFFont_correctDirection(PyFF_Font *self, PyObject *UNUSED(args)) {
17879     int i, gid;
17880     FontViewBase *fv;
17881     SplineFont *sf;
17882     EncMap *map;
17883     int changed, refchanged;
17884     int checkrefs = true;
17885     RefChar *ref;
17886     SplineChar *sc;
17887 
17888     if ( CheckIfFontClosed(self) )
17889 return (NULL);
17890     fv = self->fv;
17891     sf = fv->sf;
17892     map = fv->map;
17893     for ( i=0; i<map->enccount; ++i ) if ( (gid=map->map[i])!=-1 && (sc=sf->glyphs[gid])!=NULL && fv->selected[i] ) {
17894 	changed = refchanged = false;
17895 	if ( checkrefs ) {
17896 	    for ( ref=sc->layers[self->fv->active_layer].refs; ref!=NULL; ref=ref->next ) {
17897 		if ( ref->transform[0]*ref->transform[3]<0 ||
17898 			(ref->transform[0]==0 && ref->transform[1]*ref->transform[2]>0)) {
17899 		    if ( !refchanged ) {
17900 			refchanged = true;
17901 			SCPreserveLayer(sc,self->fv->active_layer,false);
17902 		    }
17903 		    SCRefToSplines(sc,ref,self->fv->active_layer);
17904 		}
17905 	    }
17906 	}
17907 	if ( !refchanged )
17908 	    SCPreserveLayer(sc,self->fv->active_layer,false);
17909 	sc->layers[self->fv->active_layer].splines = SplineSetsCorrect(sc->layers[self->fv->active_layer].splines,&changed);
17910 	if ( changed || refchanged )
17911 	    SCCharChangedUpdate(sc,self->fv->active_layer);
17912     }
17913 Py_RETURN( self );
17914 }
17915 
PyFFFont_RemoveOverlap(PyFF_Font * self,PyObject * UNUSED (args))17916 static PyObject *PyFFFont_RemoveOverlap(PyFF_Font *self, PyObject *UNUSED(args)) {
17917     if ( CheckIfFontClosed(self) )
17918 return (NULL);
17919     FVOverlap(self->fv,over_remove);
17920 Py_RETURN( self );
17921 }
17922 
PyFFFont_Intersect(PyFF_Font * self,PyObject * UNUSED (args))17923 static PyObject *PyFFFont_Intersect(PyFF_Font *self, PyObject *UNUSED(args)) {
17924     if ( CheckIfFontClosed(self) )
17925 return (NULL);
17926     FVOverlap(self->fv,over_intersect);
17927 Py_RETURN( self );
17928 }
17929 
PyFFFont_replaceWithReference(PyFF_Font * self,PyObject * args)17930 static PyObject *PyFFFont_replaceWithReference(PyFF_Font *self, PyObject *args) {
17931     double fudge = .01;
17932 
17933     if ( CheckIfFontClosed(self) )
17934 return (NULL);
17935     if ( !PyArg_ParseTuple(args,"|d",&fudge) )
17936 return( NULL );
17937 
17938     FVBReplaceOutlineWithReference(self->fv,fudge);
17939 Py_RETURN( self );
17940 }
17941 
PyFFFont_correctReferences(PyFF_Font * self,PyObject * UNUSED (args))17942 static PyObject *PyFFFont_correctReferences(PyFF_Font *self, PyObject *UNUSED(args)) {
17943 
17944     FVCorrectReferences(self->fv);
17945 Py_RETURN( self );
17946 }
17947 
PyFFFont_validate(PyFF_Font * self,PyObject * args)17948 static PyObject *PyFFFont_validate(PyFF_Font *self, PyObject *args) {
17949     FontViewBase *fv;
17950     SplineFont *sf;
17951     int force=false;
17952 
17953     if ( CheckIfFontClosed(self) )
17954 return (NULL);
17955     fv = self->fv;
17956     sf = fv->sf;
17957     if ( !PyArg_ParseTuple(args,"|i",&force) )
17958 return( NULL );
17959 return( Py_BuildValue("i", SFValidate(sf,fv->active_layer,force)));
17960 }
17961 
PyFFFont_reencode(PyFF_Font * self,PyObject * args)17962 static PyObject *PyFFFont_reencode(PyFF_Font *self, PyObject *args) {
17963     int force=0;
17964     const char *encname;
17965     int ret;
17966 
17967     if ( CheckIfFontClosed(self) )
17968 return ( NULL );
17969     if ( !PyArg_ParseTuple(args, "s|i", &encname, &force) )
17970 return ( NULL );
17971 
17972     ret = SFReencode(self->fv->sf, encname, force);
17973     if ( ret==-1 ) {
17974 	PyErr_Format(PyExc_NameError, "Unknown encoding %s", encname);
17975 return ( NULL );
17976     }
17977 
17978 Py_RETURN( self );
17979 }
17980 
PyFFFont_clearSpecialData(PyFF_Font * self,PyObject * UNUSED (args))17981 static PyObject *PyFFFont_clearSpecialData(PyFF_Font *self, PyObject *UNUSED(args)) {
17982     if ( CheckIfFontClosed(self) )
17983 return (NULL);
17984     FVClearSpecialData(self->fv);
17985 Py_RETURN( self );
17986 }
17987 
17988 PyMethodDef PyFF_Font_methods[] = {
17989     { "appendSFNTName", (PyCFunction) PyFFFont_appendSFNTName, METH_VARARGS, "Adds or replaces a name in the sfnt 'name' table. Takes three arguments, a language, a string id, and the string value" },
17990     { "close", (PyCFunction) PyFFFont_close, METH_NOARGS, "Frees up memory for the current font. Any python pointers to it will become invalid." },
17991     { "compareFonts", (PyCFunction) PyFFFont_compareFonts, METH_VARARGS, "Compares two fonts and stores the result into a file"},
17992     { "save", (PyCFunction) PyFFFont_Save, METH_VARARGS, "Save the current font to a sfd file" },
17993     { "generate", (PyCFunction) PyFFFont_Generate, METH_VARARGS | METH_KEYWORDS, "Save the current font to a standard font file" },
17994     { "generateTtc", (PyCFunction) PyFFFont_GenerateTTC, METH_VARARGS | METH_KEYWORDS, "Save the current font and some others into a truetype collection file" },
17995     { "generateFeatureFile", (PyCFunction) PyFFFont_GenerateFeature, METH_VARARGS, "Creates an adobe feature file containing all features and lookups" },
17996     { "mergeKern", (PyCFunction) PyFFFont_MergeKern, METH_VARARGS, "Merge feature data into the current font from an external file" },
17997     { "mergeFeature", (PyCFunction) PyFFFont_MergeKern, METH_VARARGS, "Merge feature data into the current font from an external file" },
17998     { "mergeFonts", (PyCFunction) PyFFFont_MergeFonts, METH_VARARGS, "Merge two fonts" },
17999     { "revert", (PyCFunction) PyFFFont_revert, METH_NOARGS, "Reloads the current font from the disk" },
18000     { "revertFromBackup", (PyCFunction) PyFFFont_revertFromBackup, METH_NOARGS, "Reloads the current font from a backup copy on the disk" },
18001     { "interpolateFonts", (PyCFunction) PyFFFont_InterpolateFonts, METH_VARARGS, "Interpolate between two fonts returning a new one" },
18002     { "createChar", (PyCFunction) PyFFFont_CreateUnicodeChar, METH_VARARGS, "Creates a (blank) glyph at the specified unicode codepoint" },
18003     { "createInterpolatedGlyph", (PyCFunction) PyFFFont_CreateInterpolatedGlyph, METH_VARARGS, "Creates (and returns) a new glyph interpolated between the two arguments." },
18004     { "createMappedChar", (PyCFunction) PyFFFont_CreateMappedChar, METH_VARARGS, "Creates a (blank) glyph at the specified encoding" },
18005     { "cidConvertTo", (PyCFunction) PyFFFont_cidConvertTo, METH_VARARGS, "Turns a normal font into a CID keyed font with one subfont." },
18006     { "cidConvertByCmap", (PyCFunction) PyFFFont_cidConvertByCmap, METH_VARARGS, "Turns a normal font into a CID keyed font with one subfont, using the CMAP file to specify the CID ordering." },
18007     { "cidFlatten", (PyCFunction) PyFFFont_cidFlatten, METH_NOARGS, "Turns a cid-keyed font into a normal font, using the CID ordering as an encoding" },
18008     { "cidFlattenByCMap", (PyCFunction) PyFFFont_cidFlattenByCMap, METH_VARARGS, "Turns a cid-keyed font into a normal font, using the mapping specified in the CMAP file." },
18009     { "cidInsertBlankSubFont", (PyCFunction) PyFFFont_cidInsertBlankSubFont, METH_NOARGS, "Adds a new subfont, a blank one, to the CID keyed font, and changes the current subfont to be the new one." },
18010     { "cidRemoveSubFont", (PyCFunction) PyFFFont_cidRemoveSubFont, METH_NOARGS, "Removes the current subfont from a CID keyed font." },
18011     { "getTableData", (PyCFunction) PyFFFont_GetTableData, METH_VARARGS, "Returns a tuple, one entry per byte (as unsigned integers) of the table"},
18012     { "setTableData", (PyCFunction) PyFFFont_SetTableData, METH_VARARGS, "Sets the table to a tuple of bytes"},
18013     { "addLookup", (PyCFunction) PyFFFont_addLookup, METH_VARARGS, "Add a new lookup"},
18014     { "addLookupSubtable", (PyCFunction) PyFFFont_addLookupSubtable, METH_VARARGS, "Add a new lookup-subtable (not for contextual lookups)"},
18015     { "addContextualSubtable", (PyCFunction) PyFFFont_addContextualSubtable, METH_VARARGS | METH_KEYWORDS, "Create a subtable in a contextual, contextual chaining or reverse contextual chaining lookup." },
18016     { "addAnchorClass", (PyCFunction) PyFFFont_addAnchorClass, METH_VARARGS, "Add a new anchor class to the subtable"},
18017     { "addKerningClass", (PyCFunction) PyFFFont_addKerningClass, METH_VARARGS, "Add a new subtable with a new kerning class to a lookup"},
18018     { "alterKerningClass", (PyCFunction) PyFFFont_alterKerningClass, METH_VARARGS, "Changes the existing kerning class in the named subtable"},
18019     { "autoKern", (PyCFunction) PyFFFont_autoKern, METH_VARARGS | METH_KEYWORDS, "Automatically generates kerning pairs between the specified sets of glyphs."},
18020     { "buildOrReplaceAALTFeatures", (PyCFunction) PyFFFont_buildOrReplaceAALTFeatures, METH_NOARGS, "Removes any existing 'aalt' features and builds new ones."},
18021     { "findEncodingSlot", (PyCFunction) PyFFFont_findEncodingSlot, METH_VARARGS, "Returns the encoding of a unicode code point or glyph name if they are in the current encoding. Else returns -1" },
18022     { "getKerningClass", (PyCFunction) PyFFFont_getKerningClass, METH_VARARGS, "Returns the contents of the kerning class in the named subtable"},
18023     { "getLookupInfo", (PyCFunction) PyFFFont_getLookupInfo, METH_VARARGS, "Get info about the named lookup" },
18024     { "getLookupSubtables", (PyCFunction) PyFFFont_getLookupSubtables, METH_VARARGS, "Get a tuple of subtable names in a lookup" },
18025     { "getLookupSubtableAnchorClasses", (PyCFunction) PyFFFont_getLookupSubtableAnchorClasses, METH_VARARGS, "Get a tuple of all anchor classes in a subtable" },
18026     { "getLookupOfSubtable", (PyCFunction) PyFFFont_getLookupOfSubtable, METH_VARARGS, "Returns the name of the lookup containing this subtable" },
18027     { "getSubtableOfAnchor", (PyCFunction) PyFFFont_getSubtableOfAnchor, METH_VARARGS, "Returns the name of the lookup subtable containing this anchor class" },
18028     { "importBitmaps", (PyCFunction) PyFFFont_importBitmaps, METH_VARARGS, "Imports bitmap strikes from a font file."},
18029     { "importLookups", (PyCFunction) PyFFFont_importLookups, METH_VARARGS, "Imports a tuple of named lookups from another font."},
18030     { "isKerningClass", (PyCFunction) PyFFFont_isKerningClass, METH_VARARGS, "Returns whether the named subtable contains a kerning class"},
18031     { "isVerticalKerning", (PyCFunction) PyFFFont_isVerticalKerning, METH_VARARGS, "Returns whether the named subtable contains vertical kerning data"},
18032     { "lookupSetFeatureList", (PyCFunction) PyFFFont_lookupSetFeatureList, METH_VARARGS, "Sets the feature, script, language list on a lookup" },
18033     { "lookupSetFlags", (PyCFunction) PyFFFont_lookupSetFlags, METH_VARARGS, "Sets the lookup flags on a lookup" },
18034     { "lookupSetStoreLigatureInAfm", (PyCFunction) PyFFFont_lookupSetStoreLigatureInAfm, METH_VARARGS, "Sets whether this ligature lookup contains data which should live in the afm file"},
18035     { "mergeLookups", (PyCFunction) PyFFFont_mergeLookups, METH_VARARGS, "Merges two lookups" },
18036     { "mergeLookupSubtables", (PyCFunction) PyFFFont_mergeLookupSubtables, METH_VARARGS, "Merges two lookup subtables" },
18037     { "printSample", (PyCFunction) PyFFFont_printSample, METH_VARARGS, "Produces a font sample printout" },
18038     { "randomText", (PyCFunction) PyFFFont_randomText, METH_VARARGS, "Produces a string with random text generated from the font using letter frequencies for the specified script and language"},
18039     { "regenBitmaps", (PyCFunction) PyFFFont_regenBitmaps, METH_VARARGS, "Rerasterize the bitmap fonts specified in the argument tuple" },
18040     { "removeAnchorClass", (PyCFunction) PyFFFont_removeAnchorClass, METH_VARARGS, "Removes the named anchor class" },
18041     { "removeGlyph", (PyCFunction) PyFFFont_removeGlyph, METH_VARARGS, "Removes the glyph from the font" },
18042     { "removeLookup", (PyCFunction) PyFFFont_removeLookup, METH_VARARGS, "Removes the named lookup" },
18043     { "removeLookupSubtable", (PyCFunction) PyFFFont_removeLookupSubtable, METH_VARARGS, "Removes the named lookup subtable" },
18044     { "saveNamelist", (PyCFunction) PyFFFont_saveNamelist, METH_VARARGS, "Saves the namelist of the current font." },
18045     { "replaceAll", (PyCFunction) PyFFFont_replaceAll, METH_VARARGS, "Searches for a pattern in the font and replaces it with another everywhere it was found" },
18046     { "find", (PyCFunction) PyFFFont_find, METH_VARARGS, "Searches for a pattern in the font and returns an iterator which produces glyphs with that pattern" },
18047     { "glyphs", (PyCFunction) PyFFFont_glyphs, METH_VARARGS, "Returns an iterator over all glyphs" },
18048 /* Selection based */
18049     { "clear", (PyCFunction) PyFFFont_clear, METH_NOARGS, "Clears all selected glyphs" },
18050     { "cut", (PyCFunction) PyFFFont_cut, METH_NOARGS, "Cuts all selected glyphs" },
18051     { "copy", (PyCFunction) PyFFFont_copy, METH_NOARGS, "Copies all selected glyphs" },
18052     { "copyReference", (PyCFunction) PyFFFont_copyReference, METH_NOARGS, "Copies all selected glyphs as references" },
18053     { "paste", (PyCFunction) PyFFFont_paste, METH_NOARGS, "Pastes the clipboard into the selected glyphs (clearing them first)" },
18054     { "pasteInto", (PyCFunction) PyFFFont_pasteInto, METH_NOARGS, "Pastes the clipboard into the selected glyphs (merging with what's there)" },
18055     { "unlinkReferences", (PyCFunction) PyFFFont_unlinkReferences, METH_NOARGS, "Unlinks all references in the selected glyphs" },
18056     { "replaceWithReference", (PyCFunction) PyFFFont_replaceWithReference, METH_VARARGS, "Replaces any inline copies of any of the selected glyphs with a reference" },
18057     { "correctReferences", (PyCFunction) PyFFFont_correctReferences, METH_NOARGS, "Replaces any inline copies of any of the selected glyphs with a reference" },
18058 
18059     { "addExtrema", (PyCFunction) PyFFFont_AddExtrema, METH_NOARGS, "Add extrema to the contours of the glyph"},
18060     { "addSmallCaps", (PyCFunction) PyFFFont_addSmallCaps, METH_VARARGS | METH_KEYWORDS, "For selected upper/lower case (latin, greek, cyrillic) characters, add a small caps variant of that glyph"},
18061     { "autoHint", (PyCFunction) PyFFFont_autoHint, METH_NOARGS, "Guess at postscript hints"},
18062     { "autoInstr", (PyCFunction) PyFFFont_autoInstr, METH_NOARGS, "Guess at truetype instructions"},
18063     { "autoWidth", (PyCFunction) PyFFFont_autoWidth, METH_VARARGS | METH_KEYWORDS, "Guess horizontal advance widths for selected glyphs" },
18064     { "autoTrace", (PyCFunction) PyFFFont_autoTrace, METH_NOARGS, "Autotrace any background images"},
18065     { "build", (PyCFunction) PyFFFont_Build, METH_NOARGS, "If the current glyph is an accented character\nand all components are in the font\nthen build it out of references" },
18066     { "canonicalContours", (PyCFunction) PyFFFont_canonicalContours, METH_NOARGS, "Orders the contours in the current glyph by the x coordinate of their leftmost point. (This can reduce the size of the postscript charstring needed to describe the glyph(s)."},
18067     { "canonicalStart", (PyCFunction) PyFFFont_canonicalStart, METH_NOARGS, "Sets the start point of all the contours of the current glyph to be the leftmost point on the contour."},
18068     { "changeWeight", (PyCFunction) PyFFFont_changeWeight, METH_VARARGS, "Change the weight (thickness) of the stems of the selected glyphs"},
18069     { "condenseExtend", (PyCFunction) PyFFFont_condenseExtend, METH_VARARGS, "Change the widths of the counters and side bearings of the selected glyphs"},
18070     { "cluster", (PyCFunction) PyFFFont_Cluster, METH_VARARGS, "Cluster the points of a glyph towards common values" },
18071     /*{ "compareGlyphs", (PyCFunction) PyFFFont_compareGlyphs, METH_VARARGS, "Compares two sets of glyphs"},*/
18072     /* compareGlyphs assumes an old scripting context */
18073     { "correctDirection", (PyCFunction) PyFFFont_correctDirection, METH_NOARGS, "Orient a layer so that external contours are clockwise and internal counter clockwise." },
18074     { "genericGlyphChange", (PyCFunction) PyFFFont_genericGlyphChange, METH_VARARGS | METH_KEYWORDS, "Rather like changeWeight or condenseExtend, called 'Change Glyph' in UI"},
18075     { "italicize", (PyCFunction) PyFFFont_italicize, METH_VARARGS | METH_KEYWORDS, "Italicize the selected glyphs"},
18076     { "intersect", (PyCFunction) PyFFFont_Intersect, METH_NOARGS, "Leaves the areas where the contours of a glyph overlap."},
18077     { "removeOverlap", (PyCFunction) PyFFFont_RemoveOverlap, METH_NOARGS, "Remove overlapping areas from a glyph"},
18078     { "round", (PyCFunction)PyFFFont_Round, METH_VARARGS, "Rounds point coordinates (and reference translations) to integers"},
18079     { "simplify", (PyCFunction)PyFFFont_Simplify, METH_VARARGS, "Simplifies a glyph" },
18080     { "stroke", (PyCFunction)PyFFFont_Stroke, METH_VARARGS | METH_KEYWORDS, "Strokes the contours in a glyph"},
18081     { "transform", (PyCFunction)PyFFFont_Transform, METH_VARARGS, "Transform a font by a 6 element matrix." },
18082     { "nltransform", (PyCFunction)PyFFFont_NLTransform, METH_VARARGS, "Transform a font by non-linear expressions for x and y." },
18083     { "validate", (PyCFunction)PyFFFont_validate, METH_VARARGS, "Check whether a font is valid and return True if it is." },
18084     { "reencode", (PyCFunction)PyFFFont_reencode, METH_VARARGS, "Reencodes the current font into the given encoding." },
18085     { "clearSpecialData", (PyCFunction)PyFFFont_clearSpecialData, METH_NOARGS, "Clear special data not accessible in FontForge." },
18086 
18087     // Leave some sentinel slots here so that the UI
18088     // code can add it's methods to the end of the object declaration.
18089     PYMETHODDEF_EMPTY,
18090     PYMETHODDEF_EMPTY,
18091     PYMETHODDEF_EMPTY,
18092     PYMETHODDEF_EMPTY,
18093     PYMETHODDEF_EMPTY,
18094     PYMETHODDEF_EMPTY,
18095     PYMETHODDEF_EMPTY,
18096     PYMETHODDEF_EMPTY,
18097     PYMETHODDEF_EMPTY,
18098     PYMETHODDEF_EMPTY,
18099     PYMETHODDEF_EMPTY,
18100     PYMETHODDEF_EMPTY,
18101     PYMETHODDEF_EMPTY,
18102     PYMETHODDEF_EMPTY,
18103     PYMETHODDEF_EMPTY,
18104     PYMETHODDEF_EMPTY,
18105     PYMETHODDEF_EMPTY,
18106     PYMETHODDEF_EMPTY,
18107     PYMETHODDEF_EMPTY,
18108 
18109     PYMETHODDEF_EMPTY /* Sentinel */
18110 };
18111 
18112 /* ************************************************************************** */
18113 /* *********************** Font as glyph dictionary ************************* */
18114 /* ************************************************************************** */
PyFF_FontLength(PyObject * object)18115 static Py_ssize_t PyFF_FontLength( PyObject *object ) {
18116     PyFF_Font *self = (PyFF_Font*)object;
18117     if ( CheckIfFontClosed(self) )
18118 return (-1);
18119 return( self->fv->map->enccount );
18120 }
18121 
PyFF_FontIndex(PyObject * object,PyObject * index)18122 static PyObject *PyFF_FontIndex( PyObject *object, PyObject *index ) {
18123     PyFF_Font *self = (PyFF_Font*)object;
18124     FontViewBase *fv;
18125     SplineFont *sf;
18126     SplineChar *sc = NULL;
18127 
18128     if ( CheckIfFontClosed(self) )
18129         return (NULL);
18130     fv = self->fv;
18131     sf = fv->sf;
18132     if ( PyUnicode_Check(index)) {
18133 	const char *name = PyUnicode_AsUTF8(index);
18134 	if (name == NULL) {
18135 	    return NULL;
18136 	}
18137 	sc = SFGetChar(sf,-1,name);
18138     } else if ( PyLong_Check(index)) {
18139 	int pos = PyLong_AsLong(index), gid;
18140 	if ( pos<0 || pos>=fv->map->enccount ) {
18141 	    PyErr_Format(PyExc_TypeError, "Index out of bounds");
18142             return( NULL );
18143 	}
18144 	gid = fv->map->map[pos];
18145 	sc = gid==-1 ? NULL : sf->glyphs[gid];
18146     } else {
18147 	PyErr_Format(PyExc_TypeError, "Index must be an integer or a string" );
18148         return( NULL );
18149     }
18150     if ( sc==NULL ) {
18151 	PyErr_Format(PyExc_TypeError, "No such glyph" );
18152         return( NULL );
18153     }
18154     return( PySC_From_SC_I(sc));
18155 }
18156 
PyFF_FontContains(PyObject * object,PyObject * index)18157 static int PyFF_FontContains( PyObject *object, PyObject *index ) {
18158     PyFF_Font *self = (PyFF_Font*)object;
18159     FontViewBase *fv;
18160     SplineFont *sf;
18161     SplineChar *sc = NULL;
18162 
18163     if ( CheckIfFontClosed(self) )
18164         return (-1);
18165     fv = self->fv;
18166     sf = fv->sf;
18167     if ( PyUnicode_Check(index)) {
18168 	const char *name = PyUnicode_AsUTF8(index);
18169 	if (name == NULL) {
18170 	    return 0;
18171 	}
18172 	sc = SFGetChar(sf,-1,name);
18173     } else if ( PyLong_Check(index)) {
18174 	int pos = PyLong_AsLong(index), gid;
18175 	if ( pos<0 || pos>=fv->map->enccount ) {
18176             return( 0 );
18177 	}
18178 	gid = fv->map->map[pos];
18179 	sc = gid==-1 ? NULL : sf->glyphs[gid];
18180     } else {
18181 	PyErr_Format(PyExc_TypeError, "Index must be an integer or a string" );
18182         return( -1 );
18183     }
18184     return( sc!=NULL );
18185 }
18186 
18187 static PySequenceMethods PyFF_FontSequence = {
18188     PyFF_FontLength,		/* length */
18189     NULL,			/* concat */
18190     NULL,			/* repeat */
18191     NULL,			/* subscript */
18192     NULL,			/* slice */
18193     NULL,			/* subscript assign */
18194     NULL,			/* slice assign */
18195     PyFF_FontContains,		/* contains */
18196     NULL,			/* inplace_concat */
18197     NULL			/* inplace repeat */
18198 };
18199 
18200 static PyMappingMethods PyFF_FontMapping = {
18201     PyFF_FontLength,		/* length */
18202     PyFF_FontIndex,		/* subscript */
18203     NULL			/* subscript assign */
18204 };
18205 
18206 static PyTypeObject PyFF_FontType = {
18207     PyVarObject_HEAD_INIT(NULL, 0)
18208     "fontforge.font",          /* tp_name */
18209     sizeof(PyFF_Font),         /* tp_basicsize */
18210     0,                         /* tp_itemsize */
18211     (destructor) PyFF_Font_dealloc, /* tp_dealloc */
18212     0,                         /* tp_vectorcall_offset */
18213     NULL,                      /* tp_getattr */
18214     NULL,                      /* tp_setattr */
18215     NULL,                      /* tp_compare */
18216     (reprfunc) PyFFFont_Repr,   /* tp_repr */
18217     NULL,                      /* tp_as_number */
18218     &PyFF_FontSequence,        /* tp_as_sequence */
18219     &PyFF_FontMapping,         /* tp_as_mapping */
18220     NULL,                      /* tp_hash */
18221     NULL,                      /* tp_call */
18222     (reprfunc) PyFFFont_Str,   /* tp_str */
18223     NULL,                      /* tp_getattro */
18224     NULL,                      /* tp_setattro */
18225     NULL,                      /* tp_as_buffer */
18226     Py_TPFLAGS_DEFAULT,        /* tp_flags */
18227     "FontForge Font object",   /* tp_doc */
18228     NULL,                      /* tp_traverse */
18229     NULL,                      /* tp_clear */
18230     NULL,                      /* tp_richcompare */
18231     0,                         /* tp_weaklistoffset */
18232     fontiter_new_wholefont,    /* tp_iter */
18233     NULL,                      /* tp_iternext */
18234     PyFF_Font_methods,         /* tp_methods */
18235     NULL,                      /* tp_members */
18236     PyFF_Font_getset,          /* tp_getset */
18237     NULL,                      /* tp_base */
18238     NULL,                      /* tp_dict */
18239     NULL,                      /* tp_descr_get */
18240     NULL,                      /* tp_descr_set */
18241     0,                         /* tp_dictoffset */
18242     /*(initproc)PyFF_Font_init*/0,  /* tp_init */
18243     0,                         /* tp_alloc */
18244     PyFF_Font_new,             /* tp_new */
18245     NULL,                      /* tp_free */
18246     NULL,                      /* tp_is_gc */
18247     NULL,                      /* tp_bases */
18248     NULL,                      /* tp_mro */
18249     NULL,                      /* tp_cache */
18250     NULL,                      /* tp_subclasses */
18251     NULL,                      /* tp_weaklist */
18252     NULL,                      /* tp_del */
18253     0,                         /* tp_version_tag */
18254 };
18255 
18256 /* ************************************************************************** */
18257 /*		     Python Interface to FontForge Auto-Kerning		      */
18258 /* ************************************************************************** */
18259 
18260 /* To give the user the ability to create his own routine to calculate the */
18261 /*  visual separation between two glyphs, we must provide a python type which */
18262 /*  contains the same data as the C type: AW_Glyph */
18263 /* Sigh. And another which is almost exactly the same for the left/right arrays*/
18264 
18265 typedef struct {
18266     PyObject_HEAD
18267     AW_Glyph *base;
18268     PyObject *left, *right;
18269 } PyFF_AWGlyph;
18270 
18271 typedef struct {
18272     PyObject_HEAD
18273     AW_Glyph *base;
18274     int is_left;
18275 } PyFF_AWGlyphI;
18276 
18277 typedef struct {
18278     PyObject_HEAD
18279     AW_Data *base;
18280     PyObject *emSize;
18281     PyObject *layer;
18282     PyObject *regionHeight;
18283     PyObject *denom;
18284 } PyFF_AWContext;
18285 static PyTypeObject PyFF_AWGlyphType, PyFF_AWGlyphIndexType, PyFF_AWContextType;
18286 
PyFF_AWGlyphIndex_dealloc(PyFF_AWGlyphI * self)18287 static void PyFF_AWGlyphIndex_dealloc(PyFF_AWGlyphI *self) {
18288     Py_TYPE(self)->tp_free((PyObject *) self);
18289 }
18290 
PyFF_AWGlyphLength(PyObject * self)18291 static Py_ssize_t PyFF_AWGlyphLength( PyObject *self ) {
18292     AW_Glyph *aw = ((PyFF_AWGlyphI *) self)->base;
18293 return( aw->imax_y - aw->imin_y + 1 );
18294 }
18295 
PyFF_AWGlyphIndex(PyObject * self,PyObject * index)18296 static PyObject *PyFF_AWGlyphIndex( PyObject *self, PyObject *index ) {
18297     AW_Glyph *aw = ((PyFF_AWGlyphI *) self)->base;
18298     int pos;
18299 
18300     if ( !PyLong_Check(index)) {
18301 	PyErr_Format(PyExc_TypeError, "Index must be an integer" );
18302 return( NULL );
18303     }
18304 
18305     pos = PyLong_AsLong(index);
18306     if ( pos<aw->imin_y || pos>aw->imax_y ) {
18307 	PyErr_Format(PyExc_TypeError, "Index out of bounds");
18308 return( NULL );
18309     }
18310     if ( ((PyFF_AWGlyphI *) self)->is_left )
18311 return( Py_BuildValue("i", aw->left[pos-aw->imin_y]) );
18312     else
18313 return( Py_BuildValue("i", aw->right[pos-aw->imin_y]) );
18314 }
18315 
18316 static PyMappingMethods PyFF_AWGlyphMapping = {
18317     PyFF_AWGlyphLength,		/* length */
18318     PyFF_AWGlyphIndex,		/* subscript */
18319     NULL			/* subscript assign */
18320 };
18321 
18322 static PyTypeObject PyFF_AWGlyphIndexType = {
18323     PyVarObject_HEAD_INIT(NULL,0)
18324     "fontforge.awglyphIndex",  /* tp_name */
18325     sizeof(PyFF_AWGlyphI),     /* tp_basicsize */
18326     0,                         /* tp_itemsize */
18327     (destructor) PyFF_AWGlyphIndex_dealloc, /* tp_dealloc */
18328     0,                         /* tp_vectorcall_offset */
18329     NULL,                      /* tp_getattr */
18330     NULL,                      /* tp_setattr */
18331     NULL,                      /* tp_compare */
18332     NULL,                      /* tp_repr */
18333     NULL,                      /* tp_as_number */
18334     NULL,                      /* tp_as_sequence */
18335     &PyFF_AWGlyphMapping,      /* tp_as_mapping */
18336     NULL,                      /* tp_hash */
18337     NULL,                      /* tp_call */
18338     NULL,                      /* tp_str */
18339     NULL,                      /* tp_getattro */
18340     NULL,                      /* tp_setattro */
18341     NULL,                      /* tp_as_buffer */
18342     Py_TPFLAGS_DEFAULT,        /* tp_flags */
18343     "FontForge index aw",      /* tp_doc */
18344     NULL,                      /* tp_traverse */
18345     NULL,                      /* tp_clear */
18346     NULL,                      /* tp_richcompare */
18347     0,                         /* tp_weaklistoffset */
18348     NULL,                      /* tp_iter */
18349     NULL,                      /* tp_iternext */
18350     NULL,                      /* tp_methods */
18351     NULL,                      /* tp_members */
18352     NULL,                      /* tp_getset */
18353     NULL,                      /* tp_base */
18354     NULL,                      /* tp_dict */
18355     NULL,                      /* tp_descr_get */
18356     NULL,                      /* tp_descr_set */
18357     0,                         /* tp_dictoffset */
18358     NULL,                      /* tp_init */
18359     NULL,                      /* tp_alloc */
18360     NULL,                      /* tp_new */
18361     NULL,                      /* tp_free */
18362     NULL,                      /* tp_is_gc */
18363     NULL,                      /* tp_bases */
18364     NULL,                      /* tp_mro */
18365     NULL,                      /* tp_cache */
18366     NULL,                      /* tp_subclasses */
18367     NULL,                      /* tp_weaklist */
18368     NULL,                      /* tp_del */
18369     0,                         /* tp_version_tag */
18370 };
18371 
PyFF_AWGlyph_dealloc(PyFF_AWGlyph * self)18372 static void PyFF_AWGlyph_dealloc(PyFF_AWGlyph *self) {
18373     Py_XDECREF(self->left);
18374     Py_XDECREF(self->right);
18375     if ( self->base!=NULL ) {
18376 	if ( self->base->python_data == self )
18377 	    self->base->python_data = NULL;
18378 	self->base = NULL;
18379     }
18380     Py_TYPE(self)->tp_free((PyObject *) self);
18381 }
18382 
GetPythonObjectForAWGlyph(AW_Glyph * aw)18383 static PyObject *GetPythonObjectForAWGlyph(AW_Glyph *aw) {
18384     if ( aw->python_data==NULL ) {
18385 	aw->python_data = PyFF_AWGlyphType.tp_alloc(&PyFF_AWGlyphType,0);
18386 	((PyFF_AWGlyph *) (aw->python_data))->base = aw;
18387 	Py_INCREF( (PyObject *) (aw->python_data) );	/* for the pointer in the aw_glyph */
18388     }
18389     Py_INCREF( (PyFF_AWGlyph *) (aw->python_data) );
18390 return( (PyObject *) (aw->python_data) );
18391 }
18392 
PyFF_AWGlyph_getGlyph(PyFF_AWGlyph * self,void * UNUSED (closure))18393 static PyObject *PyFF_AWGlyph_getGlyph(PyFF_AWGlyph *self, void *UNUSED(closure)) {
18394     PyObject *ret;
18395     if ( self->base->sc==NULL )
18396 	ret = Py_None;
18397     else
18398 	ret = PySC_From_SC(self->base->sc);
18399     Py_INCREF( ret );
18400 return( ret );
18401 }
18402 
PyFF_AWGlyph_getBB(PyFF_AWGlyph * self,void * UNUSED (closure))18403 static PyObject *PyFF_AWGlyph_getBB(PyFF_AWGlyph *self, void *UNUSED(closure)) {
18404 return( Py_BuildValue("(dddd)", self->base->bb.minx,self->base->bb.miny,
18405 	    self->base->bb.maxx,self->base->bb.maxy ));
18406 }
18407 
PyFF_AWGlyph_getIminY(PyFF_AWGlyph * self,void * UNUSED (closure))18408 static PyObject *PyFF_AWGlyph_getIminY(PyFF_AWGlyph *self, void *UNUSED(closure)) {
18409 return( Py_BuildValue("i", self->base->imin_y ));
18410 }
18411 
PyFF_AWGlyph_getImaxY(PyFF_AWGlyph * self,void * UNUSED (closure))18412 static PyObject *PyFF_AWGlyph_getImaxY(PyFF_AWGlyph *self, void *UNUSED(closure)) {
18413 return( Py_BuildValue("i", self->base->imax_y ));
18414 }
18415 
PyFF_AWGlyph_getLeft(PyFF_AWGlyph * self,void * UNUSED (closure))18416 static PyObject *PyFF_AWGlyph_getLeft(PyFF_AWGlyph *self, void *UNUSED(closure)) {
18417     if ( self->left==NULL ) {
18418 	self->left = PyFF_AWGlyphIndexType.tp_alloc(&PyFF_AWGlyphIndexType,0);
18419 	((PyFF_AWGlyphI *) (self->left))->base = self->base;
18420 	((PyFF_AWGlyphI *) (self->left))->is_left = true;
18421 	Py_INCREF( self->left );	/* for the pointer in the aw_glyph */
18422     }
18423     Py_INCREF( self->left );
18424 return( self->left );
18425 }
18426 
PyFF_AWGlyph_getRight(PyFF_AWGlyph * self,void * UNUSED (closure))18427 static PyObject *PyFF_AWGlyph_getRight(PyFF_AWGlyph *self, void *UNUSED(closure)) {
18428     if ( self->right==NULL ) {
18429 	self->right = PyFF_AWGlyphIndexType.tp_alloc(&PyFF_AWGlyphIndexType,0);
18430 	((PyFF_AWGlyphI *) (self->right))->base = self->base;
18431 	((PyFF_AWGlyphI *) (self->right))->is_left = false;
18432 	Py_INCREF( self->right );	/* for the pointer in the aw_glyph */
18433     }
18434     Py_INCREF( self->right );
18435 return( self->right );
18436 }
18437 
18438 static PyGetSetDef PyFF_AWGlyph_getset[] = {
18439     {(char *)"glyph",
18440      (getter)PyFF_AWGlyph_getGlyph, NULL,
18441      (char *)"The underlying glyph which this object describes", NULL},
18442     {(char *)"boundingbox",
18443      (getter)PyFF_AWGlyph_getBB, NULL,
18444      (char *)"The bounding box of the underlying glyph", NULL},
18445     {(char *)"iminY",
18446      (getter)PyFF_AWGlyph_getIminY, NULL,
18447      (char *)"floor(bb.min_y/decimation_height)", NULL},
18448     {(char *)"imaxY",
18449      (getter)PyFF_AWGlyph_getImaxY, NULL,
18450      (char *)"ceil(bb.max_y/decimation_height)", NULL},
18451     {(char *)"left",
18452      (getter)PyFF_AWGlyph_getLeft, NULL,
18453      (char *)"array with left edge offsets from bounding box", NULL},
18454     {(char *)"right",
18455      (getter)PyFF_AWGlyph_getRight, NULL,
18456      (char *)"array with left edge offsets from bounding box", NULL},
18457     PYGETSETDEF_EMPTY /* Sentinel */
18458 };
18459 
18460 static PyTypeObject PyFF_AWGlyphType = {
18461     PyVarObject_HEAD_INIT(NULL,0)
18462     "fontforge.awglyph",       /* tp_name */
18463     sizeof(PyFF_AWGlyph),      /* tp_basicsize */
18464     0,                         /* tp_itemsize */
18465     (destructor) PyFF_AWGlyph_dealloc, /* tp_dealloc */
18466     0,                         /* tp_vectorcall_offset */
18467     NULL,                      /* tp_getattr */
18468     NULL,                      /* tp_setattr */
18469     NULL,                      /* tp_compare */
18470     NULL,                      /* tp_repr */
18471     NULL,                      /* tp_as_number */
18472     NULL,                      /* tp_as_sequence */
18473     NULL,                      /* tp_as_mapping */    /* Need separate left/right version so needs a new type. Sigh. */
18474     NULL,                      /* tp_hash */
18475     NULL,                      /* tp_call */
18476     NULL,                      /* tp_str */
18477     NULL,                      /* tp_getattro */
18478     NULL,                      /* tp_setattro */
18479     NULL,                      /* tp_as_buffer */
18480     Py_TPFLAGS_DEFAULT,        /* tp_flags */
18481     "FontForge Auto Width/Kern Glyph object",   /* tp_doc */
18482     NULL,                      /* tp_traverse */
18483     NULL,                      /* tp_clear */
18484     NULL,                      /* tp_richcompare */
18485     0,                         /* tp_weaklistoffset */
18486     NULL,                      /* tp_iter */
18487     NULL,                      /* tp_iternext */
18488     NULL,                      /* tp_methods */
18489     NULL,                      /* tp_members */
18490     PyFF_AWGlyph_getset,       /* tp_getset */
18491     NULL,                      /* tp_base */
18492     NULL,                      /* tp_dict */
18493     NULL,                      /* tp_descr_get */
18494     NULL,                      /* tp_descr_set */
18495     0,                         /* tp_dictoffset */
18496     NULL,                      /* tp_init */
18497     NULL,                      /* tp_alloc */
18498     NULL, /*PyFF_AWGlyph_new*/ /* tp_new */
18499     NULL,                      /* tp_free */
18500     NULL,                      /* tp_is_gc */
18501     NULL,                      /* tp_bases */
18502     NULL,                      /* tp_mro */
18503     NULL,                      /* tp_cache */
18504     NULL,                      /* tp_subclasses */
18505     NULL,                      /* tp_weaklist */
18506     NULL,                      /* tp_del */
18507     0,                         /* tp_version_tag */
18508 };
18509 
PyFF_AWContext_dealloc(PyFF_AWContext * self)18510 static void PyFF_AWContext_dealloc(PyFF_AWContext *self) {
18511     Py_XDECREF(self->emSize);
18512     Py_XDECREF(self->layer);
18513     Py_XDECREF(self->regionHeight);
18514     Py_XDECREF(self->denom);
18515     if ( self->base!=NULL ) {
18516 	self->base->python_data = NULL;
18517 	self->base = NULL;
18518     }
18519     Py_TYPE(self)->tp_free((PyObject *) self);
18520 }
18521 
GetPythonObjectForAWData(AW_Data * all)18522 static PyObject *GetPythonObjectForAWData(AW_Data *all) {
18523     if ( all->python_data==NULL ) {
18524 	all->python_data = PyFF_AWContextType.tp_alloc(&PyFF_AWContextType,0);
18525 	((PyFF_AWContext *) (all->python_data))->base = all;
18526 	Py_INCREF( (PyObject *) (all->python_data) );	/* for the pointer in the aw_glyph */
18527     }
18528     Py_INCREF( (PyFF_AWContext *) (all->python_data) );
18529 return( (PyObject *) (all->python_data) );
18530 }
18531 
PyFF_AWContext_getFont(PyFF_AWContext * self,void * UNUSED (closure))18532 static PyObject *PyFF_AWContext_getFont(PyFF_AWContext *self, void *UNUSED(closure)) {
18533     return PyFF_FontForFV( self->base->fv );
18534 }
18535 
PyFF_AWContext_getEmSize(PyFF_AWContext * self,void * UNUSED (closure))18536 static PyObject *PyFF_AWContext_getEmSize(PyFF_AWContext *self, void *UNUSED(closure)) {
18537     if ( self->emSize==NULL )
18538 	self->emSize = PyLong_FromLong(self->base->sf->ascent+self->base->sf->descent);
18539     Py_INCREF( self->emSize );
18540 return( self->emSize );
18541 }
18542 
PyFF_AWContext_getLayer(PyFF_AWContext * self,void * UNUSED (closure))18543 static PyObject *PyFF_AWContext_getLayer(PyFF_AWContext *self, void *UNUSED(closure)) {
18544     if ( self->layer==NULL )
18545 	self->layer = PyLong_FromLong(self->base->layer);
18546     Py_INCREF( self->layer );
18547 return( self->layer );
18548 }
18549 
PyFF_AWContext_getRegionHeight(PyFF_AWContext * self,void * UNUSED (closure))18550 static PyObject *PyFF_AWContext_getRegionHeight(PyFF_AWContext *self, void *UNUSED(closure)) {
18551     if ( self->regionHeight==NULL )
18552 	self->regionHeight = PyLong_FromLong(self->base->sub_height);
18553     Py_INCREF( self->regionHeight );
18554 return( self->regionHeight );
18555 }
18556 
PyFF_AWContext_getDenom(PyFF_AWContext * self,void * UNUSED (closure))18557 static PyObject *PyFF_AWContext_getDenom(PyFF_AWContext *self, void *UNUSED(closure)) {
18558     if ( self->denom==NULL )
18559 	self->denom = PyFloat_FromDouble(self->base->denom);
18560     Py_INCREF( self->denom );
18561 return( self->denom );
18562 }
18563 
18564 static PyGetSetDef PyFF_AWContext_getset[] = {
18565     {(char *)"font",
18566      (getter)PyFF_AWContext_getFont, NULL,
18567      (char *)"The underlying font which this object describes", NULL},
18568     {(char *)"emSize",
18569      (getter)PyFF_AWContext_getEmSize, NULL,
18570      (char *)"Font's em-size", NULL},
18571     {(char *)"layer",
18572      (getter)PyFF_AWContext_getLayer, NULL,
18573      (char *)"active layer during the current operation", NULL},
18574     {(char *)"regionHeight",
18575      (getter)PyFF_AWContext_getRegionHeight, NULL,
18576      (char *)"The y coordinate line is subdivided into regions, and this is the height of each region", NULL},
18577     {(char *)"denom",
18578      (getter)PyFF_AWContext_getDenom, NULL,
18579      (char *)"A useful small number which varies with the emsize", NULL},
18580     PYGETSETDEF_EMPTY /* Sentinel */
18581 };
18582 
18583 static PyTypeObject PyFF_AWContextType = {
18584     PyVarObject_HEAD_INIT(NULL,0)
18585     "fontforge.awcontext",     /* tp_name */
18586     sizeof(PyFF_AWContext),    /* tp_basicsize */
18587     0,                         /* tp_itemsize */
18588     (destructor) PyFF_AWContext_dealloc, /* tp_dealloc */
18589     0,                         /* tp_vectorcall_offset */
18590     NULL,                      /* tp_getattr */
18591     NULL,                      /* tp_setattr */
18592     NULL,                      /* tp_compare */
18593     NULL,                      /* tp_repr */
18594     NULL,                      /* tp_as_number */
18595     NULL,                      /* tp_as_sequence */
18596     NULL,                      /* tp_as_mapping */        /* Need separate left/right version so needs a new type. Sigh. */
18597     NULL,                      /* tp_hash */
18598     NULL,                      /* tp_call */
18599     NULL,                      /* tp_str */
18600     NULL,                      /* tp_getattro */
18601     NULL,                      /* tp_setattro */
18602     NULL,                      /* tp_as_buffer */
18603     Py_TPFLAGS_DEFAULT,        /* tp_flags */
18604     "FontForge Auto Width/Kern Context object", /* tp_doc */
18605     NULL,                      /* tp_traverse */
18606     NULL,                      /* tp_clear */
18607     NULL,                      /* tp_richcompare */
18608     0,                         /* tp_weaklistoffset */
18609     NULL,                      /* tp_iter */
18610     NULL,                      /* tp_iternext */
18611     NULL,                      /* tp_methods */
18612     NULL,                      /* tp_members */
18613     PyFF_AWContext_getset,     /* tp_getset */
18614     NULL,                      /* tp_base */
18615     NULL,                      /* tp_dict */
18616     NULL,                      /* tp_descr_get */
18617     NULL,                      /* tp_descr_set */
18618     0,                         /* tp_dictoffset */
18619     NULL,                      /* tp_init */
18620     NULL,                      /* tp_alloc */
18621     NULL,                      /* tp_new */
18622     NULL,                      /* tp_free */
18623     NULL,                      /* tp_is_gc */
18624     NULL,                      /* tp_bases */
18625     NULL,                      /* tp_mro */
18626     NULL,                      /* tp_cache */
18627     NULL,                      /* tp_subclasses */
18628     NULL,                      /* tp_weaklist */
18629     NULL,                      /* tp_del */
18630     0,                         /* tp_version_tag */
18631 };
18632 
18633 /* User supplied python function which calculates the optical separation between */
18634 /*  two glyphs when placed so that there bounding boxes are contiguous. I assume */
18635 /*  that optical separation is linear (so if I move the bounding boxes by 3 em */
18636 /*  then the optical separation also increases by 3 em) */
18637 void *PyFF_GlyphSeparationHook = NULL;		/* Actually a python object */
18638 static PyObject *PyFF_GlyphSeparationArg = NULL;
18639 
PyFF_GlyphSeparation(AW_Glyph * g1,AW_Glyph * g2,AW_Data * all)18640 int PyFF_GlyphSeparation(AW_Glyph *g1,AW_Glyph *g2,AW_Data *all) {
18641     PyObject *arglist, *result;
18642     int ret;
18643 
18644     if ( PyFF_GlyphSeparationHook==NULL )
18645 return( -1 );
18646 
18647     arglist = PyTuple_New(PyFF_GlyphSeparationArg!=NULL &&
18648 			    PyFF_GlyphSeparationArg!=Py_None?4:3);
18649     Py_XINCREF((PyObject *) PyFF_GlyphSeparationHook);
18650     PyTuple_SetItem(arglist,0,GetPythonObjectForAWGlyph(g1));
18651     PyTuple_SetItem(arglist,1,GetPythonObjectForAWGlyph(g2));
18652     PyTuple_SetItem(arglist,2,GetPythonObjectForAWData(all));
18653     if ( PyFF_GlyphSeparationArg!=NULL &&
18654 			    PyFF_GlyphSeparationArg!=Py_None ) {
18655 	PyTuple_SetItem(arglist,3,PyFF_GlyphSeparationArg);
18656 	Py_XINCREF(PyFF_GlyphSeparationArg);
18657     }
18658     result = PyEval_CallObject(PyFF_GlyphSeparationHook, arglist);
18659     Py_DECREF(arglist);
18660     if ( PyErr_Occurred()!=NULL ) {
18661 	PyErr_Print();
18662 	Py_XDECREF(result);
18663 return( -1 );
18664     } else {
18665 	ret = PyLong_AsLong(result);
18666 	Py_XDECREF(result);
18667 	if ( PyErr_Occurred()!=NULL ) {
18668 	    PyErr_Print();
18669 return( -1 );
18670 	}
18671 return( ret );
18672     }
18673 }
18674 
PyFF_registerGlyphSeparationHook(PyObject * UNUSED (self),PyObject * args)18675 static PyObject *PyFF_registerGlyphSeparationHook(PyObject *UNUSED(self), PyObject *args) {
18676     PyObject *name;	/* Ignored for now */
18677     PyObject *hook, *arg=NULL;
18678 
18679     if ( !PyArg_ParseTuple(args,"O|OO",&hook, &arg, &name ) )
18680 return( NULL );
18681     if ( hook==Py_None ) {
18682 	Py_XDECREF((PyObject *) PyFF_GlyphSeparationHook);
18683 	Py_XDECREF( PyFF_GlyphSeparationArg);
18684 	PyFF_GlyphSeparationHook = NULL;
18685 	PyFF_GlyphSeparationArg = NULL;
18686     } else if (!PyCallable_Check((PyObject *) hook)) {
18687 	PyErr_Format(PyExc_TypeError, "First argument is not callable" );
18688 return( NULL );
18689     } else {
18690 	Py_XDECREF((PyObject *) PyFF_GlyphSeparationHook);
18691 	Py_XDECREF( PyFF_GlyphSeparationArg);
18692 	PyFF_GlyphSeparationHook = hook;
18693 	Py_XINCREF((PyObject *) PyFF_GlyphSeparationHook);
18694 	if ( arg==Py_None )
18695 	    arg = NULL;
18696 	PyFF_GlyphSeparationArg = arg;
18697 	Py_XINCREF(PyFF_GlyphSeparationArg);
18698     }
18699 
18700 Py_RETURN_NONE;
18701 }
18702 
FFPy_AWGlyphFree(AW_Glyph * me)18703 void FFPy_AWGlyphFree(AW_Glyph *me) {
18704     Py_XDECREF((PyObject *) me->python_data);
18705 }
18706 
FFPy_AWDataFree(AW_Data * all)18707 void FFPy_AWDataFree(AW_Data *all) {
18708     Py_XDECREF((PyObject *) all->python_data);
18709 }
18710 /* ************************************************************************** */
18711 /*			     FontForge Python Module			      */
18712 /* ************************************************************************** */
18713 PyMethodDef module_fontforge_methods[] = {
18714     { "getPrefs", PyFF_GetPrefs, METH_VARARGS, "Get FontForge preference items" },
18715     { "setPrefs", PyFF_SetPrefs, METH_VARARGS, "Set FontForge preference items" },
18716     { "savePrefs", PyFF_SavePrefs, METH_NOARGS, "Save FontForge preference items" },
18717     { "loadPrefs", PyFF_LoadPrefs, METH_NOARGS, "Load FontForge preference items" },
18718     { "hasSpiro", PyFF_hasSpiro, METH_NOARGS, "Returns whether this fontforge has access to Raph Levien's spiro package"},
18719     { "SpiroVersion", PyFF_SpiroVersion, METH_NOARGS, "Return Spiro Library Version" },
18720     { "onAppClosing", PyFF_onAppClosing, METH_VARARGS, "add a python function which is called when fontforge is closing down"},
18721     { "defaultOtherSubrs", PyFF_DefaultOtherSubrs, METH_NOARGS, "Use FontForge's default \"othersubrs\" functions for Type1 fonts" },
18722     { "readOtherSubrsFile", PyFF_ReadOtherSubrsFile, METH_VARARGS, "Read from a file, \"othersubrs\" functions for Type1 fonts" },
18723     { "loadEncodingFile", PyFF_LoadEncodingFile, METH_VARARGS, "Load an encoding file into the list of encodings" },
18724     { "loadNamelist", PyFF_LoadNamelist, METH_VARARGS, "Load a namelist into the list of namelists" },
18725     { "loadNamelistDir", PyFF_LoadNamelistDir, METH_VARARGS, "Load a directory of namelist files into the list of namelists" },
18726     { "preloadCidmap", PyFF_PreloadCidmap, METH_VARARGS, "Load a cidmap file" },
18727     { "unicodeFromName", PyFF_UnicodeFromName, METH_VARARGS, "Given a name, look it up in the namelists and find what unicode code point it maps to (returns -1 if not found)" },
18728     { "nameFromUnicode", PyFF_NameFromUnicode, METH_VARARGS, "Given a unicode code point and (optionally) a namelist, find the corresponding glyph name" },
18729     /* --start of libuninameslist functions------------------------ */
18730     { "UnicodeNameFromLib", PyFF_UnicodeNameFromLib, METH_VARARGS, "Return the www.unicode.org name for a given unicode character value" },
18731     { "UnicodeAnnotationFromLib", PyFF_UnicodeAnnotationFromLib, METH_VARARGS, "Return the www.unicode.org annotation(s) for a given unicode character value" },
18732     { "UnicodeBlockCountFromLib", PyFF_UnicodeBlockCountFromLib, METH_NOARGS, "Return the www.unicode.org block count" },
18733     { "UnicodeBlockStartFromLib", PyFF_UnicodeBlockStartFromLib, METH_VARARGS, "Return the www.unicode.org block start, for example block[0]={0..127} -> 0" },
18734     { "UnicodeBlockEndFromLib", PyFF_UnicodeBlockEndFromLib, METH_VARARGS, "Return the www.unicode.org block end, for example block[1]={128..255} -> 255" },
18735     { "UnicodeBlockNameFromLib", PyFF_UnicodeBlockNameFromLib, METH_VARARGS, "Return the www.unicode.org block name, for example block[2]={256..383} -> Latin Extended-A" },
18736     { "UnicodeNamesListVersion", PyFF_UnicodeNamesListVersion, METH_NOARGS, "Return the www.unicode.org NamesList version for this library" },
18737     { "UnicodeNames2GetCntFromLib", PyFF_UnicodeNames2GetCntFromLib, METH_NOARGS, "Return the www.unicode.org NamesList total count of Names2 corrections for this library" },
18738     { "UnicodeNames2NxtUniFromLib", PyFF_UnicodeNames2NxtUniFromLib, METH_VARARGS, "Return the table location of the next www.unicode.org Names2 for this library" },
18739     { "UnicodeNames2GetNxtFromLib", PyFF_UnicodeNames2GetNxtFromLib, METH_VARARGS, "Return the table location of the next www.unicode.org Names2 for this library" },
18740     { "UnicodeNames2FrmTabFromLib", PyFF_UnicodeNames2FrmTabFromLib, METH_VARARGS, "Return the www.unicode.org NamesList Names2 from internal table[0<=N<UnicodeNames2GetCnt()] for this library" },
18741     { "UnicodeNames2FromLib", PyFF_UnicodeNames2FromLib, METH_VARARGS, "Return the www.unicode.org NamesList Names2 for this Unicode value if it exists for this library" },
18742     { "scriptFromUnicode", PyFF_scriptFromUnicode, METH_VARARGS, "Return the script tag for the given Unicode codepoint. So, 'Q' would return \"latn\"." },
18743     /* --end of libuninameslist functions-------------------------- */
18744     { "IsFraction", PyFF_isfraction, METH_VARARGS, "Compare value with internal Vulgar_Fraction and Other_Fraction table. Return true/false" },
18745     { "IsLigature", PyFF_isligature, METH_VARARGS, "Compare value with internal Ligature table. Return true/false" },
18746     { "IsVulgarFraction", PyFF_isvulgarfraction, METH_VARARGS, "Compare value with internal Vulgar_Fraction table. Return true/false" },
18747     { "IsOtherFraction", PyFF_isotherfraction, METH_VARARGS, "Compare value with internal Other_Fraction table. Return true/false" },
18748     { "ucLigChartGetCnt", PyFF_LigChartGetCnt, METH_NOARGS, "Return internal www.unicode.org chart Ligature count" },
18749     { "ucVulChartGetCnt", PyFF_VulChartGetCnt, METH_NOARGS, "Return internal www.unicode.org chart Vulgar_Fractions count" },
18750     { "ucOFracChartGetCnt", PyFF_OFracChartGetCnt, METH_NOARGS, "Return internal www.unicode.org chart Other_Fractions count" },
18751     { "ucFracChartGetCnt", PyFF_FracChartGetCnt, METH_NOARGS, "Return internal www.unicode.org chart {Vulgar+Other} Fractions count" },
18752     { "ucLigChartGetNxt", PyFF_LigChartGetNxt, METH_VARARGS, "Return internal array unicode value Ligature[n]" },
18753     { "ucVulChartGetNxt", PyFF_VulChartGetNxt, METH_VARARGS, "Return internal array unicode value Vulgar_Fraction[n]" },
18754     { "ucOFracChartGetNxt", PyFF_OFracChartGetNxt, METH_VARARGS, "Return internal array unicode value Other_Fraction[n]" },
18755     { "ucLigChartGetLoc", PyFF_LigChartGetLoc, METH_VARARGS, "Return internal array location n for given unicode Ligature value" },
18756     { "ucVulChartGetLoc", PyFF_VulChartGetLoc, METH_VARARGS, "Return internal array location n for given unicode Vulgar_Fraction value" },
18757     { "ucOFracChartGetLoc", PyFF_OFracChartGetLoc, METH_VARARGS, "Return internal array location n for given unicode Other_Fraction value" },
18758     { "ucLigChartGetAltCnt", PyFF_LigChartGetAltCnt, METH_VARARGS, "Return internal Alternate count for given unicode Ligature value" },
18759     { "ucLigChartGetAltVal", PyFF_LigChartGetAltVal, METH_VARARGS, "Return internal Alternate value for given unicode Ligature value" },
18760     { "ucVulChartGetAltCnt", PyFF_VulChartGetAltCnt, METH_VARARGS, "Return internal Alternate count for given unicode Vulgar_Fraction value" },
18761     { "ucVulChartGetAltVal", PyFF_VulChartGetAltVal, METH_VARARGS, "Return internal Alternate value for given unicode Vulgar_Fraction value" },
18762     { "ucOFracChartGetAltCnt", PyFF_OFracChartGetAltCnt, METH_VARARGS, "Return internal Alternate count for given unicode Other_Fraction value" },
18763     { "ucOFracChartGetAltVal", PyFF_OFracChartGetAltVal, METH_VARARGS, "Return internal Alternate value for given unicode Other_Fraction value" },
18764     { "ucLigChartUGetAltCnt", PyFF_LigChartUGetAltCnt, METH_VARARGS, "Return internal Alternate count for given unicode Ligature value" },
18765     { "ucLigChartUGetAltVal", PyFF_LigChartUGetAltVal, METH_VARARGS, "Return internal Alternate value for given unicode Ligature value" },
18766     { "ucVulChartUGetAltCnt", PyFF_VulChartUGetAltCnt, METH_VARARGS, "Return internal Alternate count for given unicode Vulgar_Fraction value" },
18767     { "ucVulChartUGetAltVal", PyFF_VulChartUGetAltVal, METH_VARARGS, "Return internal Alternate value for given unicode Vulgar_Fraction value" },
18768     { "ucOFracChartUGetAltCnt", PyFF_OFracChartUGetAltCnt, METH_VARARGS, "Return internal Alternate count for given unicode Other_Fraction value" },
18769     { "ucOFracChartUGetAltVal", PyFF_OFracChartUGetAltVal, METH_VARARGS, "Return internal Alternate value for given unicode Other_Fraction value" },
18770     { "version", PyFF_Version, METH_NOARGS, "Returns a string containing the current version of FontForge, as 20061116" },
18771     { "runInitScripts", PyFF_RunInitScripts, METH_NOARGS, "Run the system and user initialization scripts, if not already run" },
18772     { "scriptPath", PyFF_GetScriptPath, METH_NOARGS, "Returns a list of the directories searched for scripts"},
18773     { "userConfigPath", PyFF_GetUserConfigPath, METH_NOARGS, "Returns the path to the user's FontForge configuration directory, which should be writable."},
18774     { "fonts", PyFF_FontTuple, METH_NOARGS, "Returns a tuple of all loaded fonts" },
18775     { "fontsInFile", PyFF_FontsInFile, METH_VARARGS, "Returns a tuple containing the names of any fonts in an external file"},
18776     { "open", PyFF_OpenFont, METH_VARARGS, "Opens a font and returns it" },
18777     { "printSetup", PyFF_printSetup, METH_VARARGS, "Prepare to print a font sample (select default printer or file, page size, etc.)" },
18778     { "parseTTInstrs", PyFF_ParseTTFInstrs, METH_VARARGS, "Takes a string and parses it into a tuple of truetype instruction bytes"},
18779     { "unParseTTInstrs", PyFF_UnParseTTFInstrs, METH_VARARGS, "Takes a tuple of truetype instruction bytes and converts to a human readable string"},
18780     { "unitShape", PyFF_unitShape, METH_VARARGS, "Takes an integer argument an returns a regular n-gon with that many sides. If the argument is positive the n-gon is inscribed on the unit circle, negative it is circumscribed, 0 the result is the unit circle, 1 a square. Behavior undefined for 2, -1, -2."},
18781     { "activeFont", PyFF_ActiveFont, METH_NOARGS, "If invoked from the UI, this returns the currently active font. When not in UI this returns None"},
18782     /* Deprecated name for the above */
18783     { "activeFontInUI", PyFF_ActiveFont, METH_NOARGS, "If invoked from the UI, this returns the currently active font. When not in UI this returns None"},
18784     { "activeGlyph", PyFF_ActiveGlyph, METH_NOARGS, "If invoked from the UI, this returns the currently active glyph (or None)"},
18785     { "activeLayer", PyFF_ActiveLayer, METH_NOARGS, "If invoked from the UI, this returns the currently active layer"},
18786     { "registerGlyphSeparationHook", PyFF_registerGlyphSeparationHook, METH_VARARGS, "registers a python routine which finds the visual separation between two glyphs. Used in autowidth/kern and optical bound setting."},
18787     /* Access to the User Interface ... if any */
18788     { "hasUserInterface", PyFF_hasUserInterface, METH_NOARGS, "Returns whether this fontforge session has a user interface (True if it has opened windows) or is just running a script (False)"},
18789     { "registerImportExport", PyFF_registerImportExport, METH_VARARGS, "Adds an import/export spline conversion module"},
18790     { "registerMenuItem", PyFF_registerMenuItemStub, METH_VARARGS, "Adds a menu item (which runs a python script) to the font or glyph (or both) windows -- in the Tools menu"},
18791     { "getConvexNib", PyFF_getConvexNib, METH_VARARGS, "Sets the specified 'Convex' to the layer/contour argument."},
18792     { "setConvexNib", PyFF_setConvexNib, METH_VARARGS, "Returns the specified 'Convex' nib as a layer."},
18793     { "logWarning", PyFF_logError, METH_VARARGS, "Adds a non-fatal message to the Warnings window"},
18794     { "postError", PyFF_postError, METH_VARARGS, "Pops up an error dialog box with the given title and message" },
18795     { "postNotice", PyFF_postNotice, METH_VARARGS, "Pops up an notice window with the given title and message" },
18796     { "openFilename", PyFF_openFilename, METH_VARARGS, "Pops up a file picker dialog asking the user for a filename to open" },
18797     { "saveFilename", PyFF_saveFilename, METH_VARARGS, "Pops up a file picker dialog asking the user for a filename to use for saving" },
18798     { "ask", PyFF_ask, METH_VARARGS, "Pops up a dialog asking the user a question and providing a set of buttons for the user to reply with" },
18799     { "askChoices", (PyCFunction)PyFF_askChoices, METH_VARARGS | METH_KEYWORDS, "Pops up a dialog asking the user a question and providing a scrolling list for the user to reply with" },
18800     { "askString", PyFF_askString, METH_VARARGS, "Pops up a dialog asking the user a question and providing a textfield for the user to reply with" },
18801     // Leave some sentinel slots here so that the UI
18802     // code can add it's methods to the end of the object declaration.
18803     PYMETHODDEF_EMPTY,
18804     PYMETHODDEF_EMPTY,
18805     PYMETHODDEF_EMPTY,
18806     PYMETHODDEF_EMPTY,
18807     PYMETHODDEF_EMPTY,
18808     PYMETHODDEF_EMPTY,
18809     PYMETHODDEF_EMPTY,
18810     PYMETHODDEF_EMPTY,
18811     PYMETHODDEF_EMPTY,
18812     PYMETHODDEF_EMPTY,
18813     PYMETHODDEF_EMPTY,
18814     PYMETHODDEF_EMPTY,
18815     PYMETHODDEF_EMPTY,
18816     PYMETHODDEF_EMPTY,
18817     PYMETHODDEF_EMPTY,
18818     PYMETHODDEF_EMPTY,
18819     PYMETHODDEF_EMPTY,
18820     PYMETHODDEF_EMPTY,
18821     PYMETHODDEF_EMPTY,
18822 
18823     PYMETHODDEF_EMPTY  /* Sentinel */
18824 };
18825 
18826 static python_type_info module_fontforge_types[] = {
18827     {&PyFF_AWContextType,	1, NULL},
18828     {&PyFF_AWGlyphIndexType,	0, NULL},
18829     {&PyFF_AWGlyphType,		1, NULL},
18830     {&PyFF_ContourIterType,	0, NULL},
18831     {&PyFF_ContourType,		1, NULL},
18832     {&PyFF_CvtIterType,		0, NULL},
18833     {&PyFF_CvtType,		1, NULL},
18834     {&PyFF_FontIterType,	0, NULL},
18835     {&PyFF_FontType,		1, NULL},
18836     {&PyFF_GlyphPenType,	1, NULL},
18837     {&PyFF_GlyphType,		1, NULL},
18838     {&PyFF_LayerArrayIterType,	0, NULL},
18839     {&PyFF_LayerArrayType,	1, NULL},
18840     {&PyFF_LayerInfoArrayIterType,	0, NULL},
18841     {&PyFF_LayerInfoArrayType,	1, NULL},
18842     {&PyFF_LayerInfoType,	1, NULL},
18843     {&PyFF_LayerIterType,	0, NULL},
18844     {&PyFF_LayerType,		1, NULL},
18845     {&PyFF_MathKernType,	1, NULL},
18846     {&PyFF_MathType,		1, setup_math_type},
18847     {&PyFF_PointType,		1, NULL},
18848     {&PyFF_PrivateIterType,	0, NULL},
18849     {&PyFF_PrivateType,		1, NULL},
18850     {&PyFF_RefArrayType,	1, NULL},
18851     {&PyFF_SelectionType,	1, NULL},
18852     TYPEINFO_EMPTY
18853 };
18854 
18855 static void AddHookDictionary( PyObject *module );
18856 static void AddSpiroConstants( PyObject *module );
FinalizeFontforgeModule(PyObject * module)18857 static void FinalizeFontforgeModule( PyObject* module ) {
18858     AddHookDictionary( module );
18859     AddPointConstants( module );
18860     AddSpiroConstants( module );
18861     PyModule_AddObject( module, "unspecifiedMathValue",
18862                         Py_BuildValue("i", TEX_UNDEF) );
18863 }
18864 
18865 static module_definition module_def_fontforge = {
18866     "fontforge",                           /* module_name */
18867     "FontForge font manipulation module.", /* docstring */
18868     module_fontforge_types,                /* types */
18869     module_fontforge_methods,              /* methods */
18870     true,                                  /* auto_import */
18871     FinalizeFontforgeModule,               /* finalize_func */
18872     MODULEDEF_RUNTIMEINFO_INIT
18873 };
18874 
18875 /* ************************************************************************** */
18876 /* ************************* initializer routines *************************** */
18877 /* ************************************************************************** */
FfPy_Replace_MenuItemStub(PyObject * (* func)(PyObject *,PyObject *))18878 void FfPy_Replace_MenuItemStub(PyObject *(*func)(PyObject *,PyObject *)) {
18879     int i;
18880     PyMethodDef *methods = module_fontforge_methods;
18881     for ( i=0; methods[i].ml_name!=NULL; ++i )
18882 	if ( strcmp(methods[i].ml_name,"registerMenuItem")==0 ) {
18883 	    methods[i].ml_meth = func;
18884 return;
18885 	}
18886 }
18887 
PyPS_Identity(PyObject * UNUSED (noself),PyObject * UNUSED (args))18888 static PyObject *PyPS_Identity(PyObject *UNUSED(noself), PyObject *UNUSED(args)) {
18889 return( Py_BuildValue("(dddddd)",  1.0, 0.0, 0.0,  1.0, 0.0, 0.0));
18890 }
18891 
PyPS_Translate(PyObject * UNUSED (noself),PyObject * args)18892 static PyObject *PyPS_Translate(PyObject *UNUSED(noself), PyObject *args) {
18893     double x,y=0;
18894 
18895     if ( !PyArg_ParseTuple(args,"d|d",&x,&y) ) {
18896 	PyErr_Clear();
18897 	if ( !PyArg_ParseTuple(args,"(dd)",&x,&y) )
18898 return( NULL );
18899     }
18900 
18901 return( Py_BuildValue("(dddddd)",  1.0, 0.0, 0.0, 1.0,  x, y));
18902 }
18903 
PyPS_Scale(PyObject * UNUSED (noself),PyObject * args)18904 static PyObject *PyPS_Scale(PyObject *UNUSED(noself), PyObject *args) {
18905     double x,y=-99999;
18906 
18907     if ( !PyArg_ParseTuple(args,"d|d",&x,&y) )
18908 return( NULL );
18909     if ( y== -99999 )
18910 	y = x;
18911 
18912 return( Py_BuildValue("(dddddd)",  x, 0.0, 0.0, y,  0.0, 0.0));
18913 }
18914 
PyPS_Rotate(PyObject * UNUSED (noself),PyObject * args)18915 static PyObject *PyPS_Rotate(PyObject *UNUSED(noself), PyObject *args) {
18916     double theta, c, s;
18917 
18918     if ( !PyArg_ParseTuple(args,"d",&theta) )
18919 return( NULL );
18920     c = cos(theta); s = sin(theta);
18921 
18922 return( Py_BuildValue("(dddddd)",  c, s, -s, c,  0.0, 0.0));
18923 }
18924 
PyPS_Skew(PyObject * UNUSED (noself),PyObject * args)18925 static PyObject *PyPS_Skew(PyObject *UNUSED(noself), PyObject *args) {
18926     double theta, t;
18927 
18928     if ( !PyArg_ParseTuple(args,"d",&theta) )
18929 return( NULL );
18930     t = tan(theta);
18931 
18932 return( Py_BuildValue("(dddddd)",  1.0, 0.0, t, 1.0,  0.0, 0.0));
18933 }
18934 
PyPS_Compose(PyObject * UNUSED (noself),PyObject * args)18935 static PyObject *PyPS_Compose(PyObject *UNUSED(noself), PyObject *args) {
18936     double m1[6], m2[6];
18937     real r1[6], r2[6], r3[6];
18938     int i;
18939     PyObject *tuple;
18940 
18941     if ( !PyArg_ParseTuple(args,"(dddddd)(dddddd)",
18942 	    &m1[0], &m1[1], &m1[2], &m1[3], &m1[4], &m1[5],
18943 	    &m2[0], &m2[1], &m2[2], &m2[3], &m2[4], &m2[5] ))
18944 return( NULL );
18945     for ( i=0; i<6; ++i ) {
18946 	r1[i] = m1[i]; r2[i] = m2[i];
18947     }
18948     MatMultiply(r1,r2,r3);
18949     tuple = PyTuple_New(6);
18950     for ( i=0; i<6; ++i )
18951 	PyTuple_SetItem(tuple,i,Py_BuildValue("d",(double) r3[i]));
18952 return( tuple );
18953 }
18954 
PyPS_Inverse(PyObject * UNUSED (noself),PyObject * args)18955 static PyObject *PyPS_Inverse(PyObject *UNUSED(noself), PyObject *args) {
18956     double m1[6];
18957     real r1[6], r3[6];
18958     int i;
18959     PyObject *tuple;
18960 
18961     if ( !PyArg_ParseTuple(args,"(dddddd)",
18962 	    &m1[0], &m1[1], &m1[2], &m1[3], &m1[4], &m1[5] ))
18963 return( NULL );
18964     for ( i=0; i<6; ++i )
18965 	r1[i] = m1[i];
18966     MatInverse(r3,r1);
18967     tuple = PyTuple_New(6);
18968     for ( i=0; i<6; ++i )
18969 	PyTuple_SetItem(tuple,i,Py_BuildValue("d",(double) r3[i]));
18970 return( tuple );
18971 }
18972 
18973 static PyMethodDef module_psMat_methods[] = {
18974     { "identity", PyPS_Identity, METH_NOARGS, "Identity transformation" },
18975     { "translate", PyPS_Translate, METH_VARARGS, "Translation transformation" },
18976     { "rotate", PyPS_Rotate, METH_VARARGS, "Rotation transformation" },
18977     { "skew", PyPS_Skew, METH_VARARGS, "Skew transformation (for making a oblique font)" },
18978     { "scale", PyPS_Scale, METH_VARARGS, "Scale transformation" },
18979     { "compose", PyPS_Compose, METH_VARARGS, "Compose two transformations (matrix multiplication)" },
18980     { "inverse", PyPS_Inverse, METH_VARARGS, "Provide an inverse transformations (not always possible)" },
18981     PYMETHODDEF_EMPTY /* Sentinel */
18982 };
18983 
18984 static module_definition module_def_psMat = {
18985     "psMat",                               /* module_name */
18986     "PostScript Matric manipulation",      /* docstring */
18987     NULL,                                  /* types */
18988     module_psMat_methods,                  /* methods */
18989     true,                                  /* auto_import */
18990     NULL,                                  /* finalize_func */
18991     MODULEDEF_RUNTIMEINFO_INIT
18992 };
18993 
18994 
PyFF_PicklerInit(void)18995 static void PyFF_PicklerInit(void) {
18996     if ( pickler==NULL ) {
18997         FontForge_InitializeEmbeddedPython();
18998         PyRun_SimpleString("import pickle\nimport __FontForge_Internals___;\n__FontForge_Internals___.initPickles(pickle.dumps, pickle.loads);");
18999     }
19000 }
19001 
PyFF_PickleTypesInit(void)19002 static void PyFF_PickleTypesInit(void) {
19003     if ( _new_point==NULL )
19004 	PyRun_SimpleString("import __FontForge_Internals___;\n__FontForge_Internals___.initPickleTypes(__FontForge_Internals___.newPoint,__FontForge_Internals___.newContour,__FontForge_Internals___.newLayer);");
19005 }
19006 
PyFF_PickleMeToString(void * pydata)19007 char *PyFF_PickleMeToString(void *pydata) {
19008     PyObject *pyobj, *arglist, *result;
19009     char *ret = NULL;
19010 
19011     PyFF_PicklerInit();
19012     pyobj = pydata;
19013     arglist = PyTuple_New(2);
19014     Py_XINCREF(pyobj);
19015     PyTuple_SetItem(arglist,0,pyobj);
19016     PyTuple_SetItem(arglist,1,Py_BuildValue("i",0));	/* ASCII protocol */
19017     result = PyEval_CallObject(pickler, arglist);
19018     Py_DECREF(arglist);
19019     if ( result!=NULL )
19020 	ret = copy(PyBytes_AsString(result));
19021     Py_XDECREF(result);
19022     if ( PyErr_Occurred()!=NULL ) {
19023 	PyErr_Print();
19024 	free(ret);
19025 return( NULL );
19026     } else
19027 return( ret );
19028 }
19029 
PyFF_UnPickleMeToObjects(char * str)19030 void *PyFF_UnPickleMeToObjects(char *str) {
19031     PyObject *arglist, *result;
19032 
19033     PyFF_PicklerInit();
19034     arglist = PyTuple_New(1);
19035     PyTuple_SetItem(arglist,0,Py_BuildValue("y",str)); /* Bytes/String object */
19036     result = PyEval_CallObject(unpickler, arglist);
19037     Py_DECREF(arglist);
19038     if ( PyErr_Occurred()!=NULL ) {
19039 	PyErr_Print();
19040 return( NULL );
19041     } else
19042 return( result );
19043 }
19044 
PyFFi_initPickles(PyObject * UNUSED (noself),PyObject * args)19045 static PyObject *PyFFi_initPickles(PyObject *UNUSED(noself), PyObject *args) {
19046 
19047     if ( !PyArg_ParseTuple(args,"OO",&pickler, &unpickler ))
19048 return( NULL );
19049     Py_INCREF(pickler); Py_INCREF(unpickler);
19050 Py_RETURN_NONE;
19051 }
19052 
PyFFi_initPickleTypes(PyObject * UNUSED (noself),PyObject * args)19053 static PyObject *PyFFi_initPickleTypes(PyObject *UNUSED(noself), PyObject *args) {
19054 
19055     if ( !PyArg_ParseTuple(args,"OOO",&_new_point, &_new_contour, &_new_layer ))
19056 return( NULL );
19057     Py_INCREF(_new_point); Py_INCREF(_new_contour); Py_INCREF(_new_layer);
19058 Py_RETURN_NONE;
19059 }
19060 
PyFFi_newPoint(PyObject * UNUSED (noself),PyObject * args)19061 static PyObject *PyFFi_newPoint(PyObject *UNUSED(noself), PyObject *args) {
19062 return( PyFFPoint_New(&PyFF_PointType,args,NULL));
19063 }
19064 
PyFFi_newContour(PyObject * UNUSED (noself),PyObject * args)19065 static PyObject *PyFFi_newContour(PyObject *UNUSED(noself), PyObject *args) {
19066     PyFF_Contour *self = (PyFF_Contour *) PyFFContour_new(&PyFF_ContourType,NULL,NULL);
19067     int i, len;
19068 
19069     if ( self==NULL )
19070 return( NULL );
19071     len = PyTuple_Size(args);
19072     if ( len<2 ) {
19073 	PyErr_Format(PyExc_TypeError, "Too few arguments");
19074 return( NULL );
19075     }
19076     self->is_quadratic = PyLong_AsLong(PyTuple_GetItem(args,0));
19077     if ( PyErr_Occurred()!=NULL )
19078 return( NULL );
19079     self->closed = PyLong_AsLong(PyTuple_GetItem(args,1));
19080     if ( PyErr_Occurred()!=NULL )
19081 return( NULL );
19082     self->pt_cnt = self->pt_max = len-2;
19083     self->points = PyMem_New(PyFF_Point *,self->pt_max);
19084     if ( self->points==NULL )
19085 return( NULL );
19086     for ( i=0; i<len-2; ++i ) {
19087 	PyObject *obj = PyTuple_GetItem(args,2+i);
19088 	if ( !PyType_IsSubtype(&PyFF_PointType, Py_TYPE(obj)) ) {
19089 	    PyErr_Format(PyExc_TypeError, "Expected FontForge points.");
19090 return( NULL );
19091 	}
19092 	Py_INCREF(obj);
19093 	self->points[i] = (PyFF_Point *) obj;
19094     }
19095 return( (PyObject *) self );
19096 }
19097 
PyFFi_newLayer(PyObject * UNUSED (noself),PyObject * args)19098 static PyObject *PyFFi_newLayer(PyObject *UNUSED(noself), PyObject *args) {
19099     PyFF_Layer *self = (PyFF_Layer *) PyFFLayer_new(&PyFF_LayerType,NULL,NULL);
19100     int i, len;
19101 
19102     if ( self==NULL )
19103 return( NULL );
19104     len = PyTuple_Size(args);
19105     if ( len<1 ) {
19106 	PyErr_Format(PyExc_TypeError, "Too few arguments");
19107 return( NULL );
19108     }
19109     self->is_quadratic = PyLong_AsLong(PyTuple_GetItem(args,0));
19110     if ( PyErr_Occurred()!=NULL )
19111 return( NULL );
19112     self->cntr_cnt = self->cntr_max = len-1;
19113     self->contours = PyMem_New(PyFF_Contour *,self->cntr_max);
19114     if ( self->contours==NULL )
19115 return( NULL );
19116     for ( i=0; i<len-1; ++i ) {
19117 	PyObject *obj = PyTuple_GetItem(args,1+i);
19118 	if ( !PyType_IsSubtype(&PyFF_ContourType, Py_TYPE(obj)) ) {
19119 	    PyErr_Format(PyExc_TypeError, "Expected FontForge Contours.");
19120 return( NULL );
19121 	}
19122 	Py_INCREF(obj);
19123 	self->contours[i] = (PyFF_Contour *) obj;
19124     }
19125 return( (PyObject *) self );
19126 }
19127 
19128 static PyMethodDef module_ff_internals_methods[] = {
19129     { "initPickles", PyFFi_initPickles, METH_VARARGS, "Set the pickle/unpickle globals so I can call them from C" },
19130     { "initPickleTypes", PyFFi_initPickleTypes, METH_VARARGS, "Set the some globals so I can call C functions from python" },
19131     { "newPoint", PyFFi_newPoint, METH_VARARGS, "Top level function to create a new point, needed (I think) for the pickler" },
19132     { "newContour", PyFFi_newContour, METH_VARARGS, "Top level function to create a new contour, needed (I think) for the pickler" },
19133     { "newLayer", PyFFi_newLayer, METH_VARARGS, "Top level function to create a new layer, needed (I think) for the pickler" },
19134     PYMETHODDEF_EMPTY /* Sentinel */
19135 };
19136 
19137 static module_definition module_def_ff_internals = {
19138     "__FontForge_Internals___",            /* module_name */
19139     "I use this to get access to certain python objects I need, and to hide some internal python functions. I don't expect users ever to care about it.",     /* docstring */
19140     NULL,                                  /* types */
19141     module_ff_internals_methods,           /* methods */
19142     false,                                 /* auto_import */
19143     NULL,                                  /* finalize_func */
19144     MODULEDEF_RUNTIMEINFO_INIT
19145 };
19146 
19147 
PyFF_ErrorString(const char * msg,const char * str)19148 void PyFF_ErrorString(const char *msg,const char *str) {
19149     char *cond = (char *) msg;
19150     if ( str!=NULL )
19151 	cond = strconcat3(msg, " ", str);
19152     PyErr_SetString(PyExc_ValueError, cond );
19153     if ( cond!=msg )
19154 	free(cond);
19155 }
19156 
PyFF_ErrorF3(const char * frmt,const char * str,int size,int depth)19157 void PyFF_ErrorF3(const char *frmt, const char *str, int size, int depth) {
19158     PyErr_Format(PyExc_ValueError, frmt, str, size, depth );
19159 }
19160 
19161 /* ************************************************************************** */
19162 /* PYTHON INITIALIZATION */
19163 /* ************************************************************************** */
19164 
19165 extern int no_windowing_ui, running_script;
19166 
19167 static void RegisterAllPyModules(void);
19168 static void CreateAllPyModules(void);
19169 
19170 static PyObject * CreatePyModule( module_definition *moddef );
19171 static void SetPythonProgramName( const char *progname /* in ASCII */ );
19172 static void SetPythonModuleMetadata( PyObject *module );
19173 static int FinalizePythonTypes( python_type_info* typelist );
19174 static int AddPythonTypesToModule( PyObject *module, python_type_info* typelist );
19175 
19176 #define ff_crmod(name) \
19177 PyMODINIT_FUNC CreatePyModule_##name(void) {\
19178     return CreatePyModule(&module_def_##name);\
19179 }
19180 
19181 /* ===== MODULE REGISTRY -- LIST OF ALL PYTHON MODULES ===== */
19182 static module_definition * all_modules[] = {
19183     &module_def_fontforge,
19184     &module_def_psMat,
19185     &module_def_ff_internals
19186 };
19187 
19188 ff_crmod(fontforge)
19189 ff_crmod(psMat)
19190 ff_crmod(ff_internals)
19191 
19192 #define NUM_MODULES (sizeof(all_modules) / sizeof(module_definition *))
19193 
19194 /* ===== END MODULE REGISTRY ===== */
19195 
19196 
19197 static const char *spiro_names[] = { "spiroG4", "spiroG2", "spiroCorner",
19198 				     "spiroLeft", "spiroRight", "spiroOpen", NULL };
19199 
19200 
FinalizePythonTypes(python_type_info * typelist)19201 static int FinalizePythonTypes(python_type_info* typelist) {
19202     int i=0;
19203 
19204     /* Build types first with custom initializers */
19205     for ( i=0; typelist[i].typeobj != NULL; ++i ) {
19206 	PyTypeObject * typ = typelist[i].typeobj;
19207 	type_initializer typ_init = typelist[i].setup_function;
19208 
19209 	if ( typ != NULL && typ_init != NULL ) {
19210 #ifdef DEBUG
19211 	    fprintf(stderr,"Building type %s\n", typ->tp_name);
19212 #endif
19213 	    if ( typ_init( typ ) < 0 ) {
19214 		fprintf(stderr,"Python initialization failed: setup of type %s failed\n",
19215 			typ->tp_name);
19216 		return -1;
19217 	    }
19218 	}
19219     }
19220 
19221     /* Let Python make the type ready */
19222     for ( i=0; typelist[i].typeobj != NULL; ++i ) {
19223 	PyTypeObject * typ = typelist[i].typeobj;
19224 
19225 #ifdef DEBUG
19226 	fprintf(stdout,"PyTypeReady(%s)\n", typ->tp_name);
19227 #endif
19228 	if ( PyType_Ready(typ) < 0 ) {
19229 	    fprintf(stderr,"Python initialization failed: PyTypeReady(%s) failed\n",
19230 		    typ->tp_name);
19231 	    return -1;
19232 	}
19233     }
19234     return 0;
19235 }
19236 
AddPythonTypesToModule(PyObject * module,python_type_info * typelist)19237 static int AddPythonTypesToModule( PyObject *module, python_type_info* typelist)
19238 {
19239     int i;
19240     for ( i=0; typelist[i].typeobj != NULL; ++i ) {
19241 	PyTypeObject * typ = typelist[i].typeobj;
19242 	int do_add = typelist[i].add_to_module;
19243 	const char *typnam;
19244 	const char *dot;
19245 
19246 	if ( ! do_add )
19247 	    continue;
19248 
19249 	/* Determine the type name, without the dotted module qualification */
19250 	typnam = typ->tp_name;
19251 	dot = strchr(typnam,'.');
19252 	if ( dot!=NULL )
19253 	    typnam = dot+1;
19254 
19255 	/* Add the type to the module */
19256 #ifdef DEBUG
19257 	fprintf(stdout,"Adding type %s as %s\n", typ->tp_name, typnam);
19258 #endif
19259 	Py_INCREF( typ );
19260 	PyModule_AddObject( module, typnam, (PyObject*)typ );
19261     }
19262     return 0;
19263 }
19264 
19265 
CreatePyModule(module_definition * mdef)19266 static PyObject* CreatePyModule( module_definition *mdef ) {
19267     PyObject *module;
19268 
19269     if ( mdef->runtime.module != NULL )
19270 	return mdef->runtime.module;
19271 
19272     if ( mdef->types != NULL && FinalizePythonTypes( mdef->types ) < 0 )
19273 	return NULL;
19274 
19275     mdef->runtime.pymod_def.m_name = mdef->module_name;
19276     mdef->runtime.pymod_def.m_doc = mdef->docstring;
19277     mdef->runtime.pymod_def.m_methods = mdef->methods;
19278     mdef->runtime.pymod_def.m_size = -1;
19279 #if PY_MAJOR_VERSION > 3 || PY_MINOR_VERSION >= 5
19280     mdef->runtime.pymod_def.m_slots = NULL;
19281 #else
19282     mdef->runtime.pymod_def.m_reload = NULL;
19283 #endif
19284     mdef->runtime.pymod_def.m_traverse = NULL;
19285     mdef->runtime.pymod_def.m_clear = NULL;
19286     mdef->runtime.pymod_def.m_free = NULL;
19287     module = PyModule_Create( &mdef->runtime.pymod_def );
19288     mdef->runtime.module = module;
19289     SetPythonModuleMetadata( module );
19290     if ( mdef->types != NULL )
19291 	AddPythonTypesToModule( module, mdef->types );
19292 
19293     if ( mdef->finalize_func != NULL )
19294 	(mdef->finalize_func)( module );
19295 
19296     return module;
19297 }
19298 
InitializePythonMainNamespace(void)19299 static PyObject *InitializePythonMainNamespace(void) {
19300     static PyObject *module_main = NULL; /* Python's __main__ namespace module */
19301     unsigned i;
19302 
19303     if ( module_main != NULL )
19304 	return module_main;
19305 
19306     module_main = PyImport_AddModule("__main__");
19307 
19308     /* Pre-import our modules */
19309     for ( i=0; i<NUM_MODULES; i++ ) {
19310 	if ( all_modules[i]->auto_import ) {
19311 	    const char *modname = all_modules[i]->module_name;
19312 	    if ( ! PyObject_HasAttrString(module_main, modname) ) {
19313 		PyObject *mod;
19314 		mod = PyImport_ImportModule( modname );
19315 		PyModule_AddObject( module_main, modname, mod );
19316 	    }
19317 	}
19318     }
19319     return module_main;
19320 }
19321 
CreateAllPyModules(void)19322 static void CreateAllPyModules(void) {
19323     for ( unsigned i=0; i<NUM_MODULES; i++ )
19324         CreatePyModule( all_modules[i] );
19325 }
19326 
19327 
RegisterAllPyModules(void)19328 static void RegisterAllPyModules(void) {
19329     /* This adds all the modules to Python's 'builtin' module list.
19330      * It allows an 'import some_module' to always work, as python
19331      * knows where to find the module in already-loaded code without
19332      * having to search the filesystem for module files/libraries.
19333      * NOTE: This should only be called from the embedded python,
19334      *       when used from the pyhook, we can't (or shouldn't) do this
19335      */
19336     module_def_fontforge.runtime.modinit_func = CreatePyModule_fontforge;
19337     module_def_psMat.runtime.modinit_func = CreatePyModule_psMat;
19338     module_def_ff_internals.runtime.modinit_func = CreatePyModule_ff_internals;
19339 
19340     for ( unsigned i=0; i<NUM_MODULES; i++ ) {
19341 	PyImport_AppendInittab( all_modules[i]->module_name,
19342 				all_modules[i]->runtime.modinit_func );
19343     }
19344 }
19345 
UnreferenceAllPyModules(void)19346 static void UnreferenceAllPyModules(void) {
19347     /* This clears references to the previously created Python modules
19348      * so that we avoid using them once they become invalid.
19349      */
19350 
19351     for ( unsigned i=0; i<NUM_MODULES; i++ ) {
19352 	all_modules[i]->runtime.module = NULL;
19353     }
19354 }
19355 
19356 static int python_initialized = 0;
19357 
FontForge_FinalizeEmbeddedPython(void)19358 void FontForge_FinalizeEmbeddedPython(void) {
19359     // static int python_initialized is declared above.
19360     if ( !python_initialized )
19361 	return;
19362 
19363     Py_Finalize();
19364     UnreferenceAllPyModules(); // We want to NULL old references.
19365     python_initialized = 0;
19366 }
19367 
19368 /* This is called to start up the embedded python interpreter */
FontForge_InitializeEmbeddedPython(void)19369 void FontForge_InitializeEmbeddedPython(void) {
19370     // static int python_initialized is declared above.
19371     if ( python_initialized )
19372 	return;
19373 
19374     SetPythonProgramName("fontforge");
19375     RegisterAllPyModules();
19376     Py_Initialize();
19377     python_initialized = 1;
19378 
19379     /* The embedded python interpreter is now functionally
19380      * "running". We can modify it to our needs.
19381      */
19382     CreateAllPyModules();
19383     InitializePythonMainNamespace();
19384 }
19385 
19386 static wchar_t ** copy_argv(char *arg0, int argc ,char **argv);
19387 
19388 /* PyFF_Main() -- This is called to run a script as the main task, by
19389  * running the command:   fontforge -script somescript.py arg1 arg2 ...
19390  *
19391  * This function is passed the entire original command line.
19392  * Before passing it to Python, it must eliminate all the
19393  * options up to and including the '-script' option, but while
19394  * preserving argv[0].
19395  */
PyFF_Main(int argc,char ** argv,int start)19396 _Noreturn void PyFF_Main(int argc,char **argv,int start) {
19397     char *arg;
19398     wchar_t **newargv;
19399     int newargc;
19400     int exitcode;
19401 
19402     no_windowing_ui = running_script = true;
19403 
19404     FontForge_InitializeEmbeddedPython();
19405     PyFF_ProcessInitFiles();
19406 
19407     /* Skip '-script' option */
19408     arg = argv[start];
19409     if ( *arg=='-' && arg[1]=='-' ) ++arg;
19410     if ( strcmp(arg,"-script")==0 )
19411 	++start;
19412 
19413     /* Make new argv array */
19414     newargc = argc - start + 1;
19415     newargv = copy_argv(argv[0], newargc-1, &argv[start] );
19416 
19417     /* Run Python */
19418     exitcode = Py_Main( newargc, newargv );
19419     FontForge_FinalizeEmbeddedPython();
19420     exit(exitcode);
19421 }
19422 
19423 
19424 /* ************************************************************************** */
19425 /* PYTHON INITIALIZATION */
19426 /* ************************************************************************** */
19427 
SetPythonProgramName(const char * progname)19428 static void SetPythonProgramName(const char *progname) {
19429     static wchar_t *saved_progname=NULL;
19430     if ( saved_progname )
19431 	free(saved_progname);
19432     saved_progname = copy_to_wide_string(progname);
19433     Py_SetProgramName(saved_progname);
19434 }
19435 
copy_argv(char * arg0,int argc,char ** argv)19436 static wchar_t ** copy_argv(char *arg0, int argc ,char **argv) {
19437     int i;
19438     wchar_t **newargv;
19439 
19440     newargv= calloc(argc+2,sizeof(char *));
19441     newargv[0] = copy_to_wide_string(arg0);
19442     if (newargv[0] == NULL) {
19443         fprintf(stderr, "argv[0] is an invalid multibyte sequence in the current locale\n");
19444         exit(1);
19445     }
19446 
19447     for ( i=0; i<argc; ++i ) {
19448 	newargv[i+1] = copy_to_wide_string(argv[i]);
19449 	if (newargv[i+1] == NULL) {
19450 	    fprintf(stderr, "argv[%d] is an invalid multibyte sequence in the current locale\n",i+1);
19451 	    exit(1);
19452 	}
19453     }
19454     newargv[argc+1] = NULL;
19455     return newargv;
19456 }
19457 
19458 /* ************************************************************************** */
19459 /* Other python environment initializations */
19460 /* ************************************************************************** */
19461 
SetPythonModuleMetadata(PyObject * module)19462 static void SetPythonModuleMetadata( PyObject *module ) {
19463     PyObject* pyver;
19464     PyObject* pydate;
19465     time_t dt = FONTFORGE_MODTIME_RAW;
19466     struct tm* modtime = gmtime(&dt);
19467 
19468     /* Make __version__ string */
19469     pyver = PyUnicode_FromFormat("%s git:%s",
19470 	     FONTFORGE_VERSION,
19471 	     FONTFORGE_GIT_VERSION );
19472     PyModule_AddObject(module, "__version__", pyver);
19473 
19474     /* Make __date__ string */
19475     pydate = PyUnicode_FromFormat("%04d-%02d-%02d",
19476 	     modtime->tm_year+1900, modtime->tm_mon+1, modtime->tm_mday );
19477     PyModule_AddObject(module, "__date__", pydate);
19478 }
19479 
AddHookDictionary(PyObject * module)19480 static void AddHookDictionary( PyObject *module ) {
19481     /* Add a dictionary in which the user may define hooks (scripts to
19482      * run) when certain events happen in fontforge, like loading a
19483      * file.
19484      */
19485     PyObject *hook_dict;
19486     hook_dict = PyDict_New();
19487     Py_INCREF(hook_dict);
19488     PyModule_AddObject(module, "hooks", hook_dict);
19489 }
19490 
AddSpiroConstants(PyObject * module)19491 static void AddSpiroConstants( PyObject *module ) {
19492     int i;
19493     /* Add constant names for the spiro point types */
19494     for ( i=0; spiro_names[i]!=NULL; ++i )
19495         PyModule_AddObject(module, spiro_names[i], Py_BuildValue("i",i+1));
19496 }
19497 
19498 
PyFF_Stdin(void)19499 _Noreturn void PyFF_Stdin(void) {
19500     no_windowing_ui = running_script = true;
19501 
19502     FontForge_InitializeEmbeddedPython();
19503     PyFF_ProcessInitFiles();
19504 
19505     if ( isatty(fileno(stdin)))
19506 	PyRun_InteractiveLoop(stdin,"<stdin>");
19507     else
19508 	PyRun_SimpleFile(stdin,"<stdin>");
19509 
19510     FontForge_FinalizeEmbeddedPython();
19511     exit(0);
19512 }
19513 
19514 
PyFF_ScriptFile(FontViewBase * fv,SplineChar * sc,char * filename)19515 void PyFF_ScriptFile(FontViewBase *fv,SplineChar *sc, char *filename) {
19516     FILE *fp;
19517     int rc;
19518 
19519     fp = fopen(filename, "rb");
19520     if ( fp==NULL ) {
19521 	fprintf(stderr, "Failed to open script \"%s\": %s\n", filename, strerror(errno));
19522 	LogError(_("Can't open %s"), filename );
19523 	return;
19524     }
19525 
19526     fv_active_in_ui = fv;		/* Make fv known to interpreter */
19527     sc_active_in_ui = sc;		/* Make sc known to interpreter */
19528     layer_active_in_ui = ly_fore;
19529     if ( fv!=NULL )
19530 	layer_active_in_ui = fv->active_layer;
19531 
19532     rc = PyRun_SimpleFileEx(fp, filename, 1/*close fp*/);
19533     if ( rc != 0 ) {
19534 	LogError(_("Execution of script %s failed"), filename );
19535     }
19536 }
19537 
PyFF_ScriptString(FontViewBase * fv,SplineChar * sc,int layer,char * str)19538 void PyFF_ScriptString(FontViewBase *fv,SplineChar *sc, int layer, char *str) {
19539 
19540     fv_active_in_ui = fv;		/* Make fv known to interpreter */
19541     sc_active_in_ui = sc;		/* Make sc known to interpreter */
19542     layer_active_in_ui = layer;
19543     if ( sc!=NULL )
19544 	PyFF_Glyph_Set_Layer(sc,layer);
19545     PyRun_SimpleString(str);
19546 }
19547 
PyFF_FreeFV(FontViewBase * fv)19548 void PyFF_FreeFV(FontViewBase *fv) {
19549     if ( fv->python_fv_object!=NULL ) {
19550 	((PyFF_Font *) (fv->python_fv_object))->fv = NULL;
19551 	Py_DECREF( (PyObject *) (fv->python_fv_object));
19552     }
19553 }
19554 
PyFF_FreeSF(SplineFont * sf)19555 void PyFF_FreeSF(SplineFont *sf) {
19556     Py_XDECREF( (PyObject *) (sf->python_persistent));
19557     Py_XDECREF( (PyObject *) (sf->python_temporary));
19558 }
19559 
PyFF_FreeSC(SplineChar * sc)19560 void PyFF_FreeSC(SplineChar *sc) {
19561     if ( sc->python_sc_object!=NULL ) {
19562 	((PyFF_Glyph *) (sc->python_sc_object))->sc = NULL;
19563 	Py_DECREF( (PyObject *) (sc->python_sc_object));
19564     }
19565 #if 0
19566     // This is now layer-specific.
19567     Py_XDECREF( (PyObject *) (sc->python_persistent));
19568 #endif // 0
19569     Py_XDECREF( (PyObject *) (sc->python_temporary));
19570 }
19571 
PyFF_FreeSCLayer(SplineChar * sc,int layer)19572 void PyFF_FreeSCLayer(SplineChar *sc, int layer) {
19573     Py_XDECREF( (PyObject *) (sc->layers[layer].python_persistent));
19574 }
19575 
PyFF_FreePythonPersistent(void * python_persistent)19576 extern void PyFF_FreePythonPersistent(void *python_persistent) {
19577     Py_XDECREF((PyObject *)python_persistent);
19578 }
19579 
GPtrArrayStrcmp(gconstpointer a,gconstpointer b)19580 static gint GPtrArrayStrcmp(gconstpointer a, gconstpointer b) {
19581     return strcmp(*(const char**)a, *(const char**)b);
19582 }
19583 
LoadFilesInPythonInitDir(char * dir)19584 static void LoadFilesInPythonInitDir(char *dir) {
19585     DIR *diro;
19586     struct dirent *ent;
19587     GPtrArray *filelist;
19588 
19589     diro = opendir(dir);
19590     if ( diro==NULL )		/* It's ok not to have any python init scripts */
19591 return;
19592 
19593     filelist = g_ptr_array_new_with_free_func(free);
19594 
19595     while ( (ent = readdir(diro))!=NULL ) {
19596 	char *pt = strrchr(ent->d_name,'.');
19597 	if ( pt==NULL )
19598     continue;
19599 	if ( strcmp(pt,".py")==0 ) {
19600         g_ptr_array_add(filelist, smprintf("%s/%s", dir, ent->d_name));
19601 	}
19602     }
19603     closedir(diro);
19604 
19605     g_ptr_array_sort(filelist, GPtrArrayStrcmp);
19606 
19607     showPythonErrors = 0;
19608     for (guint i = 0; i < filelist->len; ++i) {
19609 	FILE *fp;
19610 	char *pathname = (char*)filelist->pdata[i];
19611 	fp = fopen( pathname, "rb" );
19612 	if ( fp==NULL ) {
19613 	    fprintf(stderr,"Failed to open script \"%s\": %s\n",pathname,strerror(errno));
19614 	    continue;
19615 	}
19616 	PyRun_SimpleFileEx(fp, pathname, 1/*close fp*/);
19617     }
19618     showPythonErrors = 1;
19619     g_ptr_array_free(filelist, true);
19620 }
19621 
dir_exists(const char * path)19622 static int dir_exists(const char* path) {
19623     struct stat st;
19624     if ( stat(path,&st)==0 && S_ISDIR(st.st_mode) )
19625 	return 1;
19626     return 0;
19627 }
19628 
default_pyinit_dirs(void)19629 static GPtrArray *default_pyinit_dirs(void) {
19630     GPtrArray *pathlist;
19631     const char *sharedir;
19632     const char *userdir;
19633     char subdir[16];
19634     char *buffer;
19635 
19636     pathlist = g_ptr_array_new_with_free_func(free);
19637     snprintf(subdir, sizeof(subdir), "python%d", PY_MAJOR_VERSION);
19638 
19639     sharedir = getFontForgeShareDir();
19640     userdir = getFontForgeUserDir(Config);
19641 
19642     if ( sharedir!=NULL ) {
19643         buffer = smprintf("%s/%s", sharedir, subdir);
19644         if ( dir_exists(buffer) ) {
19645             g_ptr_array_add(pathlist, buffer);
19646         }
19647         else { /* Fall back to version-less python */
19648             free(buffer);
19649             buffer = smprintf("%s/%s", sharedir, "python");
19650             if ( dir_exists(buffer) ) {
19651                 g_ptr_array_add(pathlist, buffer);
19652             } else {
19653                 free(buffer);
19654             }
19655         }
19656     }
19657 
19658     if ( userdir!=NULL ) {
19659         buffer = smprintf("%s/%s", userdir, subdir);
19660         if ( dir_exists(buffer) ) {
19661             g_ptr_array_add(pathlist, buffer);
19662         }
19663         else { /* Fall back to version-less python */
19664             free(buffer);
19665             buffer = smprintf("%s/%s", userdir, "python");
19666             if ( dir_exists(buffer) ) {
19667                 g_ptr_array_add(pathlist, buffer);
19668             } else {
19669                 free(buffer);
19670             }
19671         }
19672     }
19673 
19674     return pathlist;
19675 }
19676 
PyFF_ProcessInitFiles(void)19677 void PyFF_ProcessInitFiles(void) {
19678     static int done = false;
19679     GPtrArray *dpath;
19680 
19681     if ( done )
19682 return;
19683     dpath = default_pyinit_dirs();
19684     for (guint i = 0; i < dpath->len; ++i ) {
19685 	LoadFilesInPythonInitDir( (char*)dpath->pdata[i] );
19686     }
19687     g_ptr_array_free(dpath, true);
19688     done = true;
19689 }
19690 
PyFF_CallDictFunc(PyObject * dict,const char * key,const char * argtypes,...)19691 void PyFF_CallDictFunc(PyObject *dict,const char *key,const char *argtypes, ... ) {
19692     PyObject *func, *arglist, *result;
19693     const char *pt;
19694     va_list ap;
19695     int i;
19696 
19697     if ( dict==NULL || !PyMapping_Check(dict) ||
19698 	 !PyMapping_HasKeyString(dict,(char *)key) ||
19699 	 (func = PyMapping_GetItemString(dict,(char *)key))==NULL )
19700 return;
19701     if ( !PyCallable_Check(func)) {
19702 	LogError(_("%s: Is not callable"), key );
19703 	Py_DECREF(func);
19704 return;
19705     }
19706     va_start(ap,argtypes);
19707 
19708     arglist = PyTuple_New(strlen(argtypes));
19709     for ( pt=argtypes, i=0; *pt; ++pt, ++i ) {
19710 	PyObject *arg;
19711 	if ( *pt=='f' )
19712 	    arg = PyFF_FontForFV_I( va_arg(ap,FontViewBase *));
19713 	else if ( *pt=='g' )
19714 	    arg = PySC_From_SC_I( va_arg(ap,SplineChar *));
19715 	else if ( *pt=='s' )
19716 	    arg = Py_BuildValue("s", va_arg(ap, char *));
19717 	else if ( *pt=='i' )
19718 	    arg = Py_BuildValue("i", va_arg(ap, int));
19719 	else if ( *pt=='n' ) {
19720 	    arg = Py_None;
19721 	    Py_INCREF(arg);
19722 	} else {
19723 	    IError("Unknown argument type in CallDictFunc" );
19724 	    arg = Py_None;
19725 	    Py_INCREF(arg);
19726 	}
19727 	PyTuple_SetItem(arglist,i,arg);
19728     }
19729     va_end(ap);
19730     result = PyEval_CallObject(func, arglist);
19731     Py_DECREF(arglist);
19732     Py_XDECREF(result);
19733     if ( PyErr_Occurred()!=NULL )
19734 	PyErr_Print();
19735 }
19736 
PyFF_InitFontHook(FontViewBase * fv)19737 void PyFF_InitFontHook(FontViewBase *fv) {
19738     /* Ok we just created a new fontview, and attached it to a splinefont */
19739     /*  We have not added a window or menu to it yet */
19740     SplineFont *sf = fv->sf;
19741     PyObject *obj;
19742 
19743     if ( fv->nextsame!=NULL )		/* Duplicate window looking at previously loaded font */
19744 return;
19745 
19746     fv_active_in_ui = fv;		/* Make fv known to interpreter */
19747     layer_active_in_ui = fv->active_layer;
19748 
19749     /* First check if it has a initScriptString in the persistent dictionary */
19750     /* (If we loaded from an sfd file) */
19751     obj = NULL;
19752     if ( sf->python_persistent!=NULL && PyMapping_Check(sf->python_persistent) &&
19753 	 PyMapping_HasKeyString(sf->python_persistent,(char *)"initScriptString") &&
19754 	 (obj = PyMapping_GetItemString(sf->python_persistent,(char *)"initScriptString"))!=NULL &&
19755 	 PyUnicode_Check(obj)) {
19756 	const char *str = PyUnicode_AsUTF8(obj);
19757 	if (str == NULL) {
19758 	    Py_DECREF(obj);
19759 	    return;
19760 	}
19761 	PyRun_SimpleString(str);
19762     }
19763     Py_XDECREF(obj);
19764 
19765     if ( sf->new )
19766 	PyFF_CallDictFunc(hook_dict,"newFontHook","f", fv );
19767     else
19768 	PyFF_CallDictFunc(hook_dict,"loadFontHook","f", fv );
19769 }
19770 
19771 
19772 /* This function is called when fontforge is being imported into
19773 ** a python process.  Actually python first invokes the wrapper
19774 ** functions in the pyhook/*.c files; and those then call this
19775 ** function.
19776 */
fontforge_python_init(const char * modulename)19777 PyMODINIT_FUNC fontforge_python_init(const char* modulename) {
19778     static int initted = false;
19779 
19780     if (!initted) {
19781         doinitFontForgeMain();
19782         no_windowing_ui = running_script = true;
19783 
19784         CreateAllPyModules();
19785 
19786         // Register the internal module
19787         PyObject* modules = PySys_GetObject("modules");
19788         PyObject* ref = PyDict_GetItemString(modules, module_def_ff_internals.module_name);
19789         if (ref == NULL) {
19790             PyDict_SetItemString(modules, module_def_ff_internals.module_name, module_def_ff_internals.runtime.module);
19791         }
19792 
19793         initted = true;
19794     }
19795 
19796     for ( unsigned i=0; i<NUM_MODULES; i++ )
19797 	if (strcmp(all_modules[i]->module_name, modulename)==0 )
19798 	    return all_modules[i]->runtime.module;
19799     return NULL;
19800 }
19801 
19802 #else
19803 #include "flaglist.h"
19804 #include "fontforgevw.h"
19805 #endif		/* _NO_PYTHON */
19806 
19807 /* These don't get translated. They are a copy of a similar list in fontinfo.c */
19808 static struct flaglist sfnt_name_str_ids[] = {
19809     { "SubFamily", 2},
19810     { "Copyright", 0},
19811     { "Family", 1},
19812     { "Fullname", 4},
19813     { "UniqueID", 3},
19814     { "Version", 5},
19815     { "PostScriptName", 6},
19816     { "Trademark", 7},
19817     { "Manufacturer", 8},
19818     { "Designer", 9},
19819     { "Descriptor", 10},
19820     { "Vendor URL", 11},
19821     { "Designer URL", 12},
19822     { "License", 13},
19823     { "License URL", 14},
19824 /* slot 15 is reserved */
19825     { "Preferred Family", 16},
19826     { "Preferred Styles", 17},
19827     { "Compatible Full", 18},
19828     { "Sample Text", 19},
19829     { "CID findfont Name", 20},
19830     { "WWS Family", 21},
19831     { "WWS Subfamily", 22},
19832     FLAGLIST_EMPTY /* Sentinel */
19833 };
19834 /* These don't get translated. They are a copy of a similar list in fontinfo.c */
19835 static struct flaglist sfnt_name_mslangs[] = {
19836     { "Afrikaans", 0x436},
19837     { "Albanian", 0x41c},
19838     { "Amharic", 0x45e},
19839     { "Arabic (Saudi Arabia)", 0x401},
19840     { "Arabic (Iraq)", 0x801},
19841     { "Arabic (Egypt)", 0xc01},
19842     { "Arabic (Libya)", 0x1001},
19843     { "Arabic (Algeria)", 0x1401},
19844     { "Arabic (Morocco)", 0x1801},
19845     { "Arabic (Tunisia)", 0x1C01},
19846     { "Arabic (Oman)", 0x2001},
19847     { "Arabic (Yemen)", 0x2401},
19848     { "Arabic (Syria)", 0x2801},
19849     { "Arabic (Jordan)", 0x2c01},
19850     { "Arabic (Lebanon)", 0x3001},
19851     { "Arabic (Kuwait)", 0x3401},
19852     { "Arabic (U.A.E.)", 0x3801},
19853     { "Arabic (Bahrain)", 0x3c01},
19854     { "Arabic (Qatar)", 0x4001},
19855     { "Armenian", 0x42b},
19856     { "Assamese", 0x44d},
19857     { "Azeri (Latin)", 0x42c},
19858     { "Azeri (Cyrillic)", 0x82c},
19859     { "Basque", 0x42d},
19860     { "Byelorussian", 0x423},
19861     { "Bengali", 0x445},
19862     { "Bengali Bangladesh", 0x845},
19863     { "Bulgarian", 0x402},
19864     { "Burmese", 0x455},
19865     { "Catalan", 0x403},
19866     { "Cambodian", 0x453},
19867     { "Cherokee", 0x45c},
19868     { "Chinese (Taiwan)", 0x404},
19869     { "Chinese (PRC)", 0x804},
19870     { "Chinese (Hong Kong)", 0xc04},
19871     { "Chinese (Singapore)", 0x1004},
19872     { "Chinese (Macau)", 0x1404},
19873     { "Croatian", 0x41a},
19874     { "Croatian Bosnia/Herzegovina", 0x101a},
19875     { "Czech", 0x405},
19876     { "Danish", 0x406},
19877     { "Divehi", 0x465},
19878     { "Dutch", 0x413},
19879     { "Flemish (Belgian Dutch)", 0x813},
19880     { "Edo", 0x466},
19881     { "English (British)", 0x809},
19882     { "English (US)", 0x409},
19883     { "English (Canada)", 0x1009},
19884     { "English (Australian)", 0xc09},
19885     { "English (New Zealand)", 0x1409},
19886     { "English (Irish)", 0x1809},
19887     { "English (South Africa)", 0x1c09},
19888     { "English (Jamaica)", 0x2009},
19889     { "English (Caribbean)", 0x2409},
19890     { "English (Belize)", 0x2809},
19891     { "English (Trinidad)", 0x2c09},
19892     { "English (Zimbabwe)", 0x3009},
19893     { "English (Philippines)", 0x3409},
19894     { "English (Indonesia)", 0x3809},
19895     { "English (Hong Kong)", 0x3c09},
19896     { "English (India)", 0x4009},
19897     { "English (Malaysia)", 0x4409},
19898     { "Estonian", 0x425},
19899     { "Faeroese", 0x438},
19900     { "Farsi", 0x429},
19901     { "Filipino", 0x464},
19902     { "Finnish", 0x40b},
19903     { "French French", 0x40c},
19904     { "French Belgium", 0x80c},
19905     { "French Canadian", 0xc0c},
19906     { "French Swiss", 0x100c},
19907     { "French Luxembourg", 0x140c},
19908     { "French Monaco", 0x180c},
19909     { "French West Indies", 0x1c0c},
19910     { "French Réunion", 0x200c},
19911     { "French D.R. Congo", 0x240c},
19912     { "French Senegal", 0x280c},
19913     { "French Camaroon", 0x2c0c},
19914     { "French Côte d'Ivoire", 0x300c},
19915     { "French Mali", 0x340c},
19916     { "French Morocco", 0x380c},
19917     { "French Haiti", 0x3c0c},
19918     { "French North Africa", 0xe40c},
19919     { "Frisian", 0x462},
19920     { "Fulfulde", 0x467},
19921     { "Gaelic (Scottish)", 0x43c},
19922     { "Gaelic (Irish)", 0x83c},
19923     { "Galician", 0x467},
19924     { "Georgian", 0x437},
19925     { "German German", 0x407},
19926     { "German Swiss", 0x807},
19927     { "German Austrian", 0xc07},
19928     { "German Luxembourg", 0x1007},
19929     { "German Liechtenstein", 0x1407},
19930     { "Greek", 0x408},
19931     { "Guarani", 0x474},
19932     { "Gujarati", 0x447},
19933     { "Hausa", 0x468},
19934     { "Hawaiian", 0x475},
19935     { "Hebrew", 0x40d},
19936     { "Hindi", 0x439},
19937     { "Hungarian", 0x40e},
19938     { "Ibibio", 0x469},
19939     { "Icelandic", 0x40f},
19940     { "Igbo", 0x470},
19941     { "Indonesian", 0x421},
19942     { "Inuktitut", 0x45d},
19943     { "Italian", 0x410},
19944     { "Italian Swiss", 0x810},
19945     { "Japanese", 0x411},
19946     { "Kannada", 0x44b},
19947     { "Kanuri", 0x471},
19948     { "Kashmiri (India)", 0x860},
19949     { "Kazakh", 0x43f},
19950     { "Khmer", 0x453},
19951     { "Kirghiz", 0x440},
19952     { "Konkani", 0x457},
19953     { "Korean", 0x412},
19954     { "Korean (Johab)", 0x812},
19955     { "Lao", 0x454},
19956     { "Latvian", 0x426},
19957     { "Latin", 0x476},
19958     { "Lithuanian", 0x427},
19959     { "Lithuanian (Classic)", 0x827},
19960     { "Macedonian", 0x42f},
19961     { "Malay", 0x43e},
19962     { "Malay (Brunei)", 0x83e},
19963     { "Malayalam", 0x44c},
19964     { "Maltese", 0x43a},
19965     { "Manipuri", 0x458},
19966     { "Maori", 0x481},
19967     { "Marathi", 0x44e},
19968     { "Mongolian (Cyrillic)", 0x450},
19969     { "Mongolian (Mongolian)", 0x850},
19970     { "Nepali", 0x461},
19971     { "Nepali (India)", 0x861},
19972     { "Norwegian (Bokmal)", 0x414},
19973     { "Norwegian (Nynorsk)", 0x814},
19974     { "Oriya", 0x448},
19975     { "Oromo", 0x472},
19976     { "Papiamentu", 0x479},
19977     { "Pashto", 0x463},
19978     { "Polish", 0x415},
19979     { "Portuguese (Portugal)", 0x416},
19980     { "Portuguese (Brasil)", 0x816},
19981     { "Punjabi (India)", 0x446},
19982     { "Punjabi (Pakistan)", 0x846},
19983     { "Quecha (Bolivia)", 0x46b},
19984     { "Quecha (Ecuador)", 0x86b},
19985     { "Quecha (Peru)", 0xc6b},
19986     { "Rhaeto-Romanic", 0x417},
19987     { "Romanian", 0x418},
19988     { "Romanian (Moldova)", 0x818},
19989     { "Russian", 0x419},
19990     { "Russian (Moldova)", 0x819},
19991     { "Sami (Lappish)", 0x43b},
19992     { "Sanskrit", 0x43b},
19993     { "Sepedi", 0x46c},
19994     { "Serbian (Cyrillic)", 0xc1a},
19995     { "Serbian (Latin)", 0x81a},
19996     { "Sindhi India", 0x459},
19997     { "Sindhi Pakistan", 0x859},
19998     { "Sinhalese", 0x45b},
19999     { "Slovak", 0x41b},
20000     { "Slovenian", 0x424},
20001     { "Sorbian", 0x42e},
20002     { "Spanish (Traditional)", 0x40a},
20003     { "Spanish Mexico", 0x80a},
20004     { "Spanish (Modern)", 0xc0a},
20005     { "Spanish (Guatemala)", 0x100a},
20006     { "Spanish (Costa Rica)", 0x140a},
20007     { "Spanish (Panama)", 0x180a},
20008     { "Spanish (Dominican Republic)", 0x1c0a},
20009     { "Spanish (Venezuela)", 0x200a},
20010     { "Spanish (Colombia)", 0x240a},
20011     { "Spanish (Peru)", 0x280a},
20012     { "Spanish (Argentina)", 0x2c0a},
20013     { "Spanish (Ecuador)", 0x300a},
20014     { "Spanish (Chile)", 0x340a},
20015     { "Spanish (Uruguay)", 0x380a},
20016     { "Spanish (Paraguay)", 0x3c0a},
20017     { "Spanish (Bolivia)", 0x400a},
20018     { "Spanish (El Salvador)", 0x440a},
20019     { "Spanish (Honduras)", 0x480a},
20020     { "Spanish (Nicaragua)", 0x4c0a},
20021     { "Spanish (Puerto Rico)", 0x500a},
20022     { "Spanish (United States)", 0x540a},
20023     { "Spanish (Latin America)", 0xe40a},
20024     { "Sutu", 0x430},
20025     { "Swahili (Kenyan)", 0x441},
20026     { "Swedish (Sweden)", 0x41d},
20027     { "Swedish (Finland)", 0x81d},
20028     { "Syriac", 0x45a},
20029     { "Tagalog", 0x464},
20030     { "Tajik", 0x428},
20031     { "Tamazight (Arabic)", 0x45f},
20032     { "Tamazight (Latin)", 0x85f},
20033     { "Tamil", 0x449},
20034     { "Tatar (Tatarstan)", 0x444},
20035     { "Telugu", 0x44a},
20036     { "Thai", 0x41e},
20037     { "Tibetan (PRC)", 0x451},
20038     { "Tibetan Bhutan", 0x851},
20039     { "Tigrinya Ethiopia", 0x473},
20040     { "Tigrinyan Eritrea", 0x873},
20041     { "Tsonga", 0x431},
20042     { "Tswana", 0x432},
20043     { "Turkish", 0x41f},
20044     { "Turkmen", 0x442},
20045     { "Uighur", 0x480},
20046     { "Ukrainian", 0x422},
20047     { "Urdu (Pakistan)", 0x420},
20048     { "Urdu (India)", 0x820},
20049     { "Uzbek (Latin)", 0x443},
20050     { "Uzbek (Cyrillic)", 0x843},
20051     { "Venda", 0x433},
20052     { "Vietnamese", 0x42a},
20053     { "Welsh", 0x452},
20054     { "Xhosa", 0x434},
20055     { "Yi", 0x478},
20056     { "Yiddish", 0x43d},
20057     { "Yoruba", 0x46a},
20058     { "Zulu", 0x435},
20059     FLAGLIST_EMPTY /* Sentinel */
20060 };
20061 
NOUI_TTFNameIds(int id)20062 const char *NOUI_TTFNameIds(int id) {
20063     int i;
20064 
20065     for ( i=0; sfnt_name_str_ids[i].name!=NULL; ++i )
20066 	if ( sfnt_name_str_ids[i].flag == id )
20067 return( (char *) sfnt_name_str_ids[i].name );
20068 
20069 return( _("Unknown") );
20070 }
20071 
NOUI_MSLangString(int language)20072 const char *NOUI_MSLangString(int language) {
20073     int i;
20074 
20075     for ( i=0; sfnt_name_mslangs[i].name!=NULL; ++i )
20076 	if ( sfnt_name_mslangs[i].flag == language )
20077 return( (char *) sfnt_name_mslangs[i].name );
20078 
20079     language &= 0xff;
20080     for ( i=0; sfnt_name_mslangs[i].name!=NULL; ++i )
20081 	if ( sfnt_name_mslangs[i].flag == language )
20082 return( (char *) sfnt_name_mslangs[i].name );
20083 
20084 return( _("Unknown") );
20085 }
20086