Merge branch 'fc-2_4_branch' to master
[platform/upstream/fontconfig.git] / src / fcname.c
1 /*
2  * $RCSId: xc/lib/fontconfig/src/fcname.c,v 1.15 2002/09/26 00:17:28 keithp Exp $
3  *
4  * Copyright © 2000 Keith Packard
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 #include "fcint.h"
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30
31 /* 
32  * Please do not change this list, it is used to initialize the object
33  * list in this order to match the FC_foo_OBJECT constants. Those 
34  * constants are written into cache files.
35  */
36
37 static const FcObjectType _FcBaseObjectTypes[] = {
38     { FC_FAMILY,        FcTypeString, },    /* 1 */
39     { FC_FAMILYLANG,    FcTypeString, },
40     { FC_STYLE,         FcTypeString, },
41     { FC_STYLELANG,     FcTypeString, },
42     { FC_FULLNAME,      FcTypeString, },
43     { FC_FULLNAMELANG,  FcTypeString, },
44     { FC_SLANT,         FcTypeInteger, },
45     { FC_WEIGHT,        FcTypeInteger, },
46     { FC_WIDTH,         FcTypeInteger, },
47     { FC_SIZE,          FcTypeDouble, },
48     { FC_ASPECT,        FcTypeDouble, },
49     { FC_PIXEL_SIZE,    FcTypeDouble, },
50     { FC_SPACING,       FcTypeInteger, },
51     { FC_FOUNDRY,       FcTypeString, },
52     { FC_ANTIALIAS,     FcTypeBool, },
53     { FC_HINT_STYLE,    FcTypeInteger, },
54     { FC_HINTING,       FcTypeBool, },
55     { FC_VERTICAL_LAYOUT,   FcTypeBool, },
56     { FC_AUTOHINT,      FcTypeBool, },
57     { FC_GLOBAL_ADVANCE,    FcTypeBool, },
58     { FC_FILE,          FcTypeString, },
59     { FC_INDEX,         FcTypeInteger, },
60     { FC_RASTERIZER,    FcTypeString, },
61     { FC_OUTLINE,       FcTypeBool, },
62     { FC_SCALABLE,      FcTypeBool, },
63     { FC_DPI,           FcTypeDouble },
64     { FC_RGBA,          FcTypeInteger, },
65     { FC_SCALE,         FcTypeDouble, },
66     { FC_MINSPACE,      FcTypeBool, },
67     { FC_CHAR_WIDTH,    FcTypeInteger },
68     { FC_CHAR_HEIGHT,   FcTypeInteger },
69     { FC_MATRIX,        FcTypeMatrix },
70     { FC_CHARSET,       FcTypeCharSet },
71     { FC_LANG,          FcTypeLangSet },
72     { FC_FONTVERSION,   FcTypeInteger },
73     { FC_CAPABILITY,    FcTypeString },
74     { FC_FONTFORMAT,    FcTypeString },
75     { FC_EMBOLDEN,      FcTypeBool },
76     { FC_EMBEDDED_BITMAP,   FcTypeBool },
77     { FC_DECORATIVE,    FcTypeBool }, /* 40 */
78 };
79
80 #define NUM_OBJECT_TYPES    (sizeof _FcBaseObjectTypes / sizeof _FcBaseObjectTypes[0])
81
82 typedef struct _FcObjectTypeList    FcObjectTypeList;
83
84 struct _FcObjectTypeList {
85     const FcObjectTypeList  *next;
86     const FcObjectType      *types;
87     int                     ntypes;
88 };
89
90 static const FcObjectTypeList _FcBaseObjectTypesList = {
91     0,
92     _FcBaseObjectTypes,
93     NUM_OBJECT_TYPES,
94 };
95
96 static const FcObjectTypeList   *_FcObjectTypes = &_FcBaseObjectTypesList;
97
98 #define OBJECT_HASH_SIZE    31
99
100 typedef struct _FcObjectBucket {
101     struct _FcObjectBucket  *next;
102     FcChar32                hash;
103     FcObject                id;
104 } FcObjectBucket;
105
106 static FcObjectBucket   *FcObjectBuckets[OBJECT_HASH_SIZE];
107
108 static FcObjectType     *FcObjects = (FcObjectType *) _FcBaseObjectTypes;
109 static int              FcObjectsNumber = NUM_OBJECT_TYPES;
110 static int              FcObjectsSize = 0;
111 static FcBool           FcObjectsInited;
112
113 static FcObjectType *
114 FcObjectInsert (const char *name, FcType type)
115 {
116     FcObjectType    *o;
117     if (FcObjectsNumber >= FcObjectsSize)
118     {
119         int             newsize = FcObjectsNumber * 2;
120         FcObjectType    *newobjects;
121         
122         if (FcObjectsSize)
123             newobjects = realloc (FcObjects, newsize * sizeof (FcObjectType));
124         else
125         {
126             newobjects = malloc (newsize * sizeof (FcObjectType));
127             if (newobjects)
128                 memcpy (newobjects, FcObjects,
129                         FcObjectsNumber * sizeof (FcObjectType));
130         }
131         if (!newobjects)
132             return NULL;
133         FcObjects = newobjects;
134         FcObjectsSize = newsize;
135     }
136     o = &FcObjects[FcObjectsNumber];
137     o->object = name;
138     o->type = type;
139     ++FcObjectsNumber;
140     return o;
141 }
142
143 static FcObject
144 FcObjectId (FcObjectType *o)
145 {
146     return o - FcObjects + 1;
147 }
148
149 static FcObjectType *
150 FcObjectFindByName (const char *object, FcBool insert)
151 {
152     FcChar32        hash = FcStringHash ((const FcChar8 *) object);
153     FcObjectBucket  **p;
154     FcObjectBucket  *b;
155     FcObjectType    *o;
156
157     if (!FcObjectsInited)
158         FcObjectInit ();
159     for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
160     {
161         o = FcObjects + b->id - 1;
162         if (b->hash == hash && !strcmp (object, (o->object)))
163             return o;
164     }
165     if (!insert)
166         return NULL;
167     /*
168      * Hook it into the hash chain
169      */
170     b = malloc (sizeof(FcObjectBucket));
171     if (!b) 
172         return NULL;
173     object = (const char *) FcStrCopy ((FcChar8 *) object);
174     if (!object) {
175         free (b);
176         return NULL;
177     }
178     o = FcObjectInsert (object, -1);
179     b->next = NULL;
180     b->hash = hash;
181     b->id = FcObjectId (o);
182     *p = b;
183     return o;
184 }
185
186 static FcObjectType *
187 FcObjectFindById (FcObject object)
188 {
189     if (1 <= object && object <= FcObjectsNumber)
190         return FcObjects + object - 1;
191     return NULL;
192 }
193
194 static FcBool
195 FcObjectHashInsert (const FcObjectType *object, FcBool copy)
196 {
197     FcChar32        hash = FcStringHash ((const FcChar8 *) object->object);
198     FcObjectBucket  **p;
199     FcObjectBucket  *b;
200     FcObjectType    *o;
201
202     if (!FcObjectsInited)
203         FcObjectInit ();
204     for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
205     {
206         o = FcObjects + b->id - 1;
207         if (b->hash == hash && !strcmp (object->object, o->object))
208             return FcFalse;
209     }
210     /*
211      * Hook it into the hash chain
212      */
213     b = malloc (sizeof(FcObjectBucket));
214     if (!b) 
215         return FcFalse;
216     if (copy)
217     {
218         o = FcObjectInsert (object->object, object->type);
219         if (!o)
220         {
221             free (b);
222             return FcFalse;
223         }
224     }
225     else
226         o = (FcObjectType *) object;
227     b->next = NULL;
228     b->hash = hash;
229     b->id = FcObjectId (o);
230     *p = b;
231     return FcTrue;
232 }
233
234 static void
235 FcObjectHashRemove (const FcObjectType *object, FcBool cleanobj)
236 {
237     FcChar32        hash = FcStringHash ((const FcChar8 *) object->object);
238     FcObjectBucket  **p;
239     FcObjectBucket  *b;
240     FcObjectType    *o;
241
242     if (!FcObjectsInited)
243         FcObjectInit ();
244     for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
245     {
246         o = FcObjects + b->id - 1;
247         if (b->hash == hash && !strcmp (object->object, o->object))
248         {
249             *p = b->next;
250             free (b);
251             if (cleanobj)
252             {
253                 /* Clean up object array */
254                 o->object = NULL;
255                 o->type = -1;
256                 while (FcObjects[FcObjectsNumber-1].object == NULL)
257                     --FcObjectsNumber;
258             }
259             break;
260         }
261     }
262 }
263
264 FcBool
265 FcNameRegisterObjectTypes (const FcObjectType *types, int ntypes)
266 {
267     int i;
268
269     for (i = 0; i < ntypes; i++)
270         if (!FcObjectHashInsert (&types[i], FcTrue))
271             return FcFalse;
272     return FcTrue;
273 }
274
275 FcBool
276 FcNameUnregisterObjectTypes (const FcObjectType *types, int ntypes)
277 {
278     int i;
279
280     for (i = 0; i < ntypes; i++)
281         FcObjectHashRemove (&types[i], FcTrue);
282     return FcTrue;
283 }
284
285 const FcObjectType *
286 FcNameGetObjectType (const char *object)
287 {
288     return FcObjectFindByName (object, FcFalse);
289 }
290
291 FcBool
292 FcObjectValidType (FcObject object, FcType type)
293 {
294     FcObjectType    *t = FcObjectFindById (object);
295
296     if (t) {
297         switch (t->type) {
298         case -1:
299             return FcTrue;
300         case FcTypeDouble:
301         case FcTypeInteger:
302             if (type == FcTypeDouble || type == FcTypeInteger)
303                 return FcTrue;
304             break;
305         case FcTypeLangSet:
306             if (type == FcTypeLangSet || type == FcTypeString)
307                 return FcTrue;
308             break;
309         default:
310             if (type == t->type)
311                 return FcTrue;
312             break;
313         }
314         return FcFalse;
315     }
316     return FcTrue;
317 }
318
319 FcObject
320 FcObjectFromName (const char * name)
321 {
322     FcObjectType    *o = FcObjectFindByName (name, FcTrue);
323
324     if (o)
325         return FcObjectId (o);
326     return 0;
327 }
328
329 FcBool
330 FcObjectInit (void)
331 {
332     int i;
333
334     if (FcObjectsInited)
335         return FcTrue;
336
337     FcObjectsInited = FcTrue;
338     for (i = 0; i < NUM_OBJECT_TYPES; i++)
339         if (!FcObjectHashInsert (&_FcBaseObjectTypes[i], FcFalse))
340             return FcFalse;
341     return FcTrue;
342 }
343
344 void
345 FcObjectFini (void)
346 {
347     int             i;
348     FcObjectBucket  *b, *next;
349
350     for (i = 0; i < OBJECT_HASH_SIZE; i++)
351     {
352         for (b = FcObjectBuckets[i]; b; b = next)
353         {
354             next = b->next;
355             free (b);
356         }
357         FcObjectBuckets[i] = 0;
358     }
359     for (i = 0; i < FcObjectsNumber; i++)
360         if (FcObjects[i].type == -1)
361             free ((void*) FcObjects[i].object);
362     if (FcObjects != _FcBaseObjectTypes)
363         free (FcObjects);
364     FcObjects = (FcObjectType *) _FcBaseObjectTypes;
365     FcObjectsNumber = NUM_OBJECT_TYPES;
366     FcObjectsSize = 0;
367     FcObjectsInited = FcFalse;
368 }
369
370 const char *
371 FcObjectName (FcObject object)
372 {
373     FcObjectType    *o = FcObjectFindById (object);
374
375     if (o)
376         return o->object;
377     return NULL;
378 }
379
380 static const FcConstant _FcBaseConstants[] = {
381     { (FcChar8 *) "thin",           "weight",   FC_WEIGHT_THIN, },
382     { (FcChar8 *) "extralight",     "weight",   FC_WEIGHT_EXTRALIGHT, },
383     { (FcChar8 *) "ultralight",     "weight",   FC_WEIGHT_EXTRALIGHT, },
384     { (FcChar8 *) "light",          "weight",   FC_WEIGHT_LIGHT, },
385     { (FcChar8 *) "book",           "weight",   FC_WEIGHT_BOOK, },
386     { (FcChar8 *) "regular",        "weight",   FC_WEIGHT_REGULAR, },
387     { (FcChar8 *) "medium",         "weight",   FC_WEIGHT_MEDIUM, },
388     { (FcChar8 *) "demibold",       "weight",   FC_WEIGHT_DEMIBOLD, },
389     { (FcChar8 *) "semibold",       "weight",   FC_WEIGHT_DEMIBOLD, },
390     { (FcChar8 *) "bold",           "weight",   FC_WEIGHT_BOLD, },
391     { (FcChar8 *) "extrabold",      "weight",   FC_WEIGHT_EXTRABOLD, },
392     { (FcChar8 *) "ultrabold",      "weight",   FC_WEIGHT_EXTRABOLD, },
393     { (FcChar8 *) "black",          "weight",   FC_WEIGHT_BLACK, },
394     { (FcChar8 *) "heavy",          "weight",   FC_WEIGHT_HEAVY, },
395
396     { (FcChar8 *) "roman",          "slant",    FC_SLANT_ROMAN, },
397     { (FcChar8 *) "italic",         "slant",    FC_SLANT_ITALIC, },
398     { (FcChar8 *) "oblique",        "slant",    FC_SLANT_OBLIQUE, },
399
400     { (FcChar8 *) "ultracondensed", "width",    FC_WIDTH_ULTRACONDENSED },
401     { (FcChar8 *) "extracondensed", "width",    FC_WIDTH_EXTRACONDENSED },
402     { (FcChar8 *) "condensed",      "width",    FC_WIDTH_CONDENSED },
403     { (FcChar8 *) "semicondensed", "width",     FC_WIDTH_SEMICONDENSED },
404     { (FcChar8 *) "normal",         "width",    FC_WIDTH_NORMAL },
405     { (FcChar8 *) "semiexpanded",   "width",    FC_WIDTH_SEMIEXPANDED },
406     { (FcChar8 *) "expanded",       "width",    FC_WIDTH_EXPANDED },
407     { (FcChar8 *) "extraexpanded",  "width",    FC_WIDTH_EXTRAEXPANDED },
408     { (FcChar8 *) "ultraexpanded",  "width",    FC_WIDTH_ULTRAEXPANDED },
409     
410     { (FcChar8 *) "proportional",   "spacing",  FC_PROPORTIONAL, },
411     { (FcChar8 *) "dual",           "spacing",  FC_DUAL, },
412     { (FcChar8 *) "mono",           "spacing",  FC_MONO, },
413     { (FcChar8 *) "charcell",       "spacing",  FC_CHARCELL, },
414
415     { (FcChar8 *) "unknown",        "rgba",         FC_RGBA_UNKNOWN },
416     { (FcChar8 *) "rgb",            "rgba",         FC_RGBA_RGB, },
417     { (FcChar8 *) "bgr",            "rgba",         FC_RGBA_BGR, },
418     { (FcChar8 *) "vrgb",           "rgba",         FC_RGBA_VRGB },
419     { (FcChar8 *) "vbgr",           "rgba",         FC_RGBA_VBGR },
420     { (FcChar8 *) "none",           "rgba",         FC_RGBA_NONE },
421
422     { (FcChar8 *) "hintnone",       "hintstyle",   FC_HINT_NONE },
423     { (FcChar8 *) "hintslight",     "hintstyle",   FC_HINT_SLIGHT },
424     { (FcChar8 *) "hintmedium",     "hintstyle",   FC_HINT_MEDIUM },
425     { (FcChar8 *) "hintfull",       "hintstyle",   FC_HINT_FULL },
426
427     { (FcChar8 *) "antialias",      "antialias",    FcTrue },
428     { (FcChar8 *) "hinting",        "hinting",      FcTrue },
429     { (FcChar8 *) "verticallayout", "verticallayout",   FcTrue },
430     { (FcChar8 *) "autohint",       "autohint",     FcTrue },
431     { (FcChar8 *) "globaladvance",  "globaladvance",    FcTrue },
432     { (FcChar8 *) "outline",        "outline",      FcTrue },
433     { (FcChar8 *) "scalable",       "scalable",     FcTrue },
434     { (FcChar8 *) "minspace",       "minspace",     FcTrue },
435     { (FcChar8 *) "embolden",       "embolden",     FcTrue },
436     { (FcChar8 *) "embeddedbitmap", "embeddedbitmap",   FcTrue },
437     { (FcChar8 *) "decorative",     "decorative",   FcTrue },
438 };
439
440 #define NUM_FC_CONSTANTS   (sizeof _FcBaseConstants/sizeof _FcBaseConstants[0])
441
442 typedef struct _FcConstantList FcConstantList;
443
444 struct _FcConstantList {
445     const FcConstantList    *next;
446     const FcConstant        *consts;
447     int                     nconsts;
448 };
449
450 static const FcConstantList _FcBaseConstantList = {
451     0,
452     _FcBaseConstants,
453     NUM_FC_CONSTANTS
454 };
455
456 static const FcConstantList     *_FcConstants = &_FcBaseConstantList;
457
458 FcBool
459 FcNameRegisterConstants (const FcConstant *consts, int nconsts)
460 {
461     FcConstantList      *l;
462
463     l = (FcConstantList *) malloc (sizeof (FcConstantList));
464     if (!l)
465         return FcFalse;
466     FcMemAlloc (FC_MEM_CONSTANT, sizeof (FcConstantList));
467     l->consts = consts;
468     l->nconsts = nconsts;
469     l->next = _FcConstants;
470     _FcConstants = l;
471     return FcTrue;
472 }
473
474 FcBool
475 FcNameUnregisterConstants (const FcConstant *consts, int nconsts)
476 {
477     const FcConstantList        *l, **prev;
478
479     for (prev = &_FcConstants; 
480          (l = *prev); 
481          prev = (const FcConstantList **) &(l->next))
482     {
483         if (l->consts == consts && l->nconsts == nconsts)
484         {
485             *prev = l->next;
486             FcMemFree (FC_MEM_CONSTANT, sizeof (FcConstantList));
487             free ((void *) l);
488             return FcTrue;
489         }
490     }
491     return FcFalse;
492 }
493
494 const FcConstant *
495 FcNameGetConstant (FcChar8 *string)
496 {
497     const FcConstantList    *l;
498     int                     i;
499
500     for (l = _FcConstants; l; l = l->next)
501     {
502         for (i = 0; i < l->nconsts; i++)
503             if (!FcStrCmpIgnoreCase (string, l->consts[i].name))
504                 return &l->consts[i];
505     }
506     return 0;
507 }
508
509 FcBool
510 FcNameConstant (FcChar8 *string, int *result)
511 {
512     const FcConstant    *c;
513
514     if ((c = FcNameGetConstant(string)))
515     {
516         *result = c->value;
517         return FcTrue;
518     }
519     return FcFalse;
520 }
521
522 FcBool
523 FcNameBool (const FcChar8 *v, FcBool *result)
524 {
525     char    c0, c1;
526
527     c0 = *v;
528     c0 = FcToLower (c0);
529     if (c0 == 't' || c0 == 'y' || c0 == '1')
530     {
531         *result = FcTrue;
532         return FcTrue;
533     }
534     if (c0 == 'f' || c0 == 'n' || c0 == '0')
535     {
536         *result = FcFalse;
537         return FcTrue;
538     }
539     if (c0 == 'o')
540     {
541         c1 = v[1];
542         c1 = FcToLower (c1);
543         if (c1 == 'n')
544         {
545             *result = FcTrue;
546             return FcTrue;
547         }
548         if (c1 == 'f')
549         {
550             *result = FcFalse;
551             return FcTrue;
552         }
553     }
554     return FcFalse;
555 }
556
557 static FcValue
558 FcNameConvert (FcType type, FcChar8 *string, FcMatrix *m)
559 {
560     FcValue     v;
561
562     v.type = type;
563     switch (v.type) {
564     case FcTypeInteger:
565         if (!FcNameConstant (string, &v.u.i))
566             v.u.i = atoi ((char *) string);
567         break;
568     case FcTypeString:
569         v.u.s = FcStrStaticName(string);
570         if (!v.u.s)
571             v.type = FcTypeVoid;
572         break;
573     case FcTypeBool:
574         if (!FcNameBool (string, &v.u.b))
575             v.u.b = FcFalse;
576         break;
577     case FcTypeDouble:
578         v.u.d = strtod ((char *) string, 0);
579         break;
580     case FcTypeMatrix:
581         v.u.m = m;
582         sscanf ((char *) string, "%lg %lg %lg %lg", &m->xx, &m->xy, &m->yx, &m->yy);
583         break;
584     case FcTypeCharSet:
585         v.u.c = FcNameParseCharSet (string);
586         if (!v.u.c)
587             v.type = FcTypeVoid;
588         break;
589     case FcTypeLangSet:
590         v.u.l = FcNameParseLangSet (string);
591         if (!v.u.l)
592             v.type = FcTypeVoid;
593         break;
594     default:
595         break;
596     }
597     return v;
598 }
599
600 static const FcChar8 *
601 FcNameFindNext (const FcChar8 *cur, const char *delim, FcChar8 *save, FcChar8 *last)
602 {
603     FcChar8    c;
604     
605     while ((c = *cur))
606     {
607         if (c == '\\')
608         {
609             ++cur;
610             if (!(c = *cur))
611                 break;
612         }
613         else if (strchr (delim, c))
614             break;
615         ++cur;
616         *save++ = c;
617     }
618     *save = 0;
619     *last = *cur;
620     if (*cur)
621         cur++;
622     return cur;
623 }
624
625 FcPattern *
626 FcNameParse (const FcChar8 *name)
627 {
628     FcChar8             *save;
629     FcPattern           *pat;
630     double              d;
631     FcChar8             *e;
632     FcChar8             delim;
633     FcValue             v;
634     FcMatrix            m;
635     const FcObjectType  *t;
636     const FcConstant    *c;
637
638     /* freed below */
639     save = malloc (strlen ((char *) name) + 1);
640     if (!save)
641         goto bail0;
642     pat = FcPatternCreate ();
643     if (!pat)
644         goto bail1;
645
646     for (;;)
647     {
648         name = FcNameFindNext (name, "-,:", save, &delim);
649         if (save[0])
650         {
651             if (!FcPatternAddString (pat, FC_FAMILY, save))
652                 goto bail2;
653         }
654         if (delim != ',')
655             break;
656     }
657     if (delim == '-')
658     {
659         for (;;)
660         {
661             name = FcNameFindNext (name, "-,:", save, &delim);
662             d = strtod ((char *) save, (char **) &e);
663             if (e != save)
664             {
665                 if (!FcPatternAddDouble (pat, FC_SIZE, d))
666                     goto bail2;
667             }
668             if (delim != ',')
669                 break;
670         }
671     }
672     while (delim == ':')
673     {
674         name = FcNameFindNext (name, "=_:", save, &delim);
675         if (save[0])
676         {
677             if (delim == '=' || delim == '_')
678             {
679                 t = FcNameGetObjectType ((char *) save);
680                 for (;;)
681                 {
682                     name = FcNameFindNext (name, ":,", save, &delim);
683                     if (t)
684                     {
685                         v = FcNameConvert (t->type, save, &m);
686                         if (!FcPatternAdd (pat, t->object, v, FcTrue))
687                         {
688                             switch (v.type) {
689                             case FcTypeCharSet:
690                                 FcCharSetDestroy ((FcCharSet *) v.u.c);
691                                 break;
692                             case FcTypeLangSet:
693                                 FcLangSetDestroy ((FcLangSet *) v.u.l);
694                                 break;
695                             default:
696                                 break;
697                             }
698                             goto bail2;
699                         }
700                         switch (v.type) {
701                         case FcTypeCharSet:
702                             FcCharSetDestroy ((FcCharSet *) v.u.c);
703                             break;
704                         case FcTypeLangSet:
705                             FcLangSetDestroy ((FcLangSet *) v.u.l);
706                             break;
707                         default:
708                             break;
709                         }
710                     }
711                     if (delim != ',')
712                         break;
713                 }
714             }
715             else
716             {
717                 if ((c = FcNameGetConstant (save)))
718                 {
719                     t = FcNameGetObjectType ((char *) c->object);
720                     switch (t->type) {
721                     case FcTypeInteger:
722                     case FcTypeDouble:
723                         if (!FcPatternAddInteger (pat, c->object, c->value))
724                             goto bail2;
725                         break;
726                     case FcTypeBool:
727                         if (!FcPatternAddBool (pat, c->object, c->value))
728                             goto bail2;
729                         break;
730                     default:
731                         break;
732                     }
733                 }
734             }
735         }
736     }
737
738     free (save);
739     return pat;
740
741 bail2:
742     FcPatternDestroy (pat);
743 bail1:
744     free (save);
745 bail0:
746     return 0;
747 }
748 static FcBool
749 FcNameUnparseString (FcStrBuf       *buf, 
750                      const FcChar8  *string,
751                      const FcChar8  *escape)
752 {
753     FcChar8 c;
754     while ((c = *string++))
755     {
756         if (escape && strchr ((char *) escape, (char) c))
757         {
758             if (!FcStrBufChar (buf, escape[0]))
759                 return FcFalse;
760         }
761         if (!FcStrBufChar (buf, c))
762             return FcFalse;
763     }
764     return FcTrue;
765 }
766
767 static FcBool
768 FcNameUnparseValue (FcStrBuf    *buf,
769                     FcValue     *v0,
770                     FcChar8     *escape)
771 {
772     FcChar8     temp[1024];
773     FcValue v = FcValueCanonicalize(v0);
774     
775     switch (v.type) {
776     case FcTypeVoid:
777         return FcTrue;
778     case FcTypeInteger:
779         sprintf ((char *) temp, "%d", v.u.i);
780         return FcNameUnparseString (buf, temp, 0);
781     case FcTypeDouble:
782         sprintf ((char *) temp, "%g", v.u.d);
783         return FcNameUnparseString (buf, temp, 0);
784     case FcTypeString:
785         return FcNameUnparseString (buf, v.u.s, escape);
786     case FcTypeBool:
787         return FcNameUnparseString (buf, v.u.b ? (FcChar8 *) "True" : (FcChar8 *) "False", 0);
788     case FcTypeMatrix:
789         sprintf ((char *) temp, "%g %g %g %g", 
790                  v.u.m->xx, v.u.m->xy, v.u.m->yx, v.u.m->yy);
791         return FcNameUnparseString (buf, temp, 0);
792     case FcTypeCharSet:
793         return FcNameUnparseCharSet (buf, v.u.c);
794     case FcTypeLangSet:
795         return FcNameUnparseLangSet (buf, v.u.l);
796     case FcTypeFTFace:
797         return FcTrue;
798     }
799     return FcFalse;
800 }
801
802 static FcBool
803 FcNameUnparseValueList (FcStrBuf        *buf,
804                         FcValueListPtr  v,
805                         FcChar8         *escape)
806 {
807     while (v)
808     {
809         if (!FcNameUnparseValue (buf, &v->value, escape))
810             return FcFalse;
811         if ((v = FcValueListNext(v)) != NULL)
812             if (!FcNameUnparseString (buf, (FcChar8 *) ",", 0))
813                 return FcFalse;
814     }
815     return FcTrue;
816 }
817
818 #define FC_ESCAPE_FIXED    "\\-:,"
819 #define FC_ESCAPE_VARIABLE "\\=_:,"
820
821 FcChar8 *
822 FcNameUnparse (FcPattern *pat)
823 {
824     return FcNameUnparseEscaped (pat, FcTrue);
825 }
826
827 FcChar8 *
828 FcNameUnparseEscaped (FcPattern *pat, FcBool escape)
829 {
830     FcStrBuf                buf;
831     FcChar8                 buf_static[8192];
832     int                     i;
833     FcPatternElt            *e;
834     const FcObjectTypeList  *l;
835     const FcObjectType      *o;
836
837     FcStrBufInit (&buf, buf_static, sizeof (buf_static));
838     e = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT);
839     if (e)
840     {
841         if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ? (FcChar8 *) FC_ESCAPE_FIXED : 0))
842             goto bail0;
843     }
844     e = FcPatternObjectFindElt (pat, FC_SIZE_OBJECT);
845     if (e)
846     {
847         if (!FcNameUnparseString (&buf, (FcChar8 *) "-", 0))
848             goto bail0;
849         if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ? (FcChar8 *) FC_ESCAPE_FIXED : 0))
850             goto bail0;
851     }
852     for (l = _FcObjectTypes; l; l = l->next)
853     {
854         for (i = 0; i < l->ntypes; i++)
855         {
856             o = &l->types[i];
857             if (!strcmp (o->object, FC_FAMILY) || 
858                 !strcmp (o->object, FC_SIZE) ||
859                 !strcmp (o->object, FC_FILE))
860                 continue;
861             
862             e = FcPatternObjectFindElt (pat, FcObjectFromName (o->object));
863             if (e)
864             {
865                 if (!FcNameUnparseString (&buf, (FcChar8 *) ":", 0))
866                     goto bail0;
867                 if (!FcNameUnparseString (&buf, (FcChar8 *) o->object, escape ? (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
868                     goto bail0;
869                 if (!FcNameUnparseString (&buf, (FcChar8 *) "=", 0))
870                     goto bail0;
871                 if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ? 
872                                              (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
873                     goto bail0;
874             }
875         }
876     }
877     return FcStrBufDone (&buf);
878 bail0:
879     FcStrBufDestroy (&buf);
880     return 0;
881 }