fcname.c revision 898dab68
1/*
2 * fontconfig/src/fcname.c
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 the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  The authors make 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 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) 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
37static 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, },	/* deprecated */
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 },
78    { FC_LCD_FILTER,	FcTypeInteger }, /* 41 */
79    { FC_NAMELANG,	FcTypeString }, /* 42 */
80};
81
82#define NUM_OBJECT_TYPES    (sizeof _FcBaseObjectTypes / sizeof _FcBaseObjectTypes[0])
83
84typedef struct _FcObjectTypeList    FcObjectTypeList;
85
86struct _FcObjectTypeList {
87    const FcObjectTypeList  *next;
88    const FcObjectType	    *types;
89    int			    ntypes;
90};
91
92static const FcObjectTypeList _FcBaseObjectTypesList = {
93    0,
94    _FcBaseObjectTypes,
95    NUM_OBJECT_TYPES,
96};
97
98static const FcObjectTypeList	*_FcObjectTypes = &_FcBaseObjectTypesList;
99
100#define OBJECT_HASH_SIZE    31
101
102typedef struct _FcObjectBucket {
103    struct _FcObjectBucket  *next;
104    FcChar32		    hash;
105    FcObject		    id;
106} FcObjectBucket;
107
108static FcObjectBucket	*FcObjectBuckets[OBJECT_HASH_SIZE];
109
110static FcObjectType	*FcObjects = (FcObjectType *) _FcBaseObjectTypes;
111static int		FcObjectsNumber = NUM_OBJECT_TYPES;
112static int		FcObjectsSize = 0;
113static FcBool		FcObjectsInited;
114
115static FcObjectType *
116FcObjectInsert (const char *name, FcType type)
117{
118    FcObjectType    *o;
119    if (FcObjectsNumber >= FcObjectsSize)
120    {
121	int		newsize = FcObjectsNumber * 2;
122	FcObjectType	*newobjects;
123
124	if (FcObjectsSize)
125	    newobjects = realloc (FcObjects, newsize * sizeof (FcObjectType));
126	else
127	{
128	    newobjects = malloc (newsize * sizeof (FcObjectType));
129	    if (newobjects)
130		memcpy (newobjects, FcObjects,
131			FcObjectsNumber * sizeof (FcObjectType));
132	}
133	if (!newobjects)
134	    return NULL;
135	FcObjects = newobjects;
136	FcObjectsSize = newsize;
137    }
138    o = &FcObjects[FcObjectsNumber];
139    o->object = name;
140    o->type = type;
141    ++FcObjectsNumber;
142    return o;
143}
144
145static FcObject
146FcObjectId (FcObjectType *o)
147{
148    return o - FcObjects + 1;
149}
150
151static FcObjectType *
152FcObjectFindByName (const char *object, FcBool insert)
153{
154    FcChar32	    hash = FcStringHash ((const FcChar8 *) object);
155    FcObjectBucket  **p;
156    FcObjectBucket  *b;
157    FcObjectType    *o;
158
159    if (!FcObjectsInited)
160	FcObjectInit ();
161    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
162    {
163	o = FcObjects + b->id - 1;
164        if (b->hash == hash && !strcmp (object, (o->object)))
165            return o;
166    }
167    if (!insert)
168	return NULL;
169    /*
170     * Hook it into the hash chain
171     */
172    b = malloc (sizeof(FcObjectBucket));
173    if (!b)
174	return NULL;
175    object = (const char *) FcStrCopy ((FcChar8 *) object);
176    if (!object) {
177	free (b);
178	return NULL;
179    }
180    o = FcObjectInsert (object, -1);
181    b->next = NULL;
182    b->hash = hash;
183    b->id = FcObjectId (o);
184    *p = b;
185    return o;
186}
187
188static FcObjectType *
189FcObjectFindById (FcObject object)
190{
191    if (1 <= object && object <= FcObjectsNumber)
192	return FcObjects + object - 1;
193    return NULL;
194}
195
196static FcBool
197FcObjectHashInsert (const FcObjectType *object, FcBool copy)
198{
199    FcChar32	    hash = FcStringHash ((const FcChar8 *) object->object);
200    FcObjectBucket  **p;
201    FcObjectBucket  *b;
202    FcObjectType    *o;
203
204    if (!FcObjectsInited)
205	FcObjectInit ();
206    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
207    {
208	o = FcObjects + b->id - 1;
209        if (b->hash == hash && !strcmp (object->object, o->object))
210            return FcFalse;
211    }
212    /*
213     * Hook it into the hash chain
214     */
215    b = malloc (sizeof(FcObjectBucket));
216    if (!b)
217	return FcFalse;
218    if (copy)
219    {
220	o = FcObjectInsert (object->object, object->type);
221	if (!o)
222	{
223	    free (b);
224	    return FcFalse;
225	}
226    }
227    else
228	o = (FcObjectType *) object;
229    b->next = NULL;
230    b->hash = hash;
231    b->id = FcObjectId (o);
232    *p = b;
233    return FcTrue;
234}
235
236static void
237FcObjectHashRemove (const FcObjectType *object, FcBool cleanobj)
238{
239    FcChar32	    hash = FcStringHash ((const FcChar8 *) object->object);
240    FcObjectBucket  **p;
241    FcObjectBucket  *b;
242    FcObjectType    *o;
243
244    if (!FcObjectsInited)
245	FcObjectInit ();
246    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
247    {
248	o = FcObjects + b->id - 1;
249        if (b->hash == hash && !strcmp (object->object, o->object))
250	{
251	    *p = b->next;
252	    free (b);
253	    if (cleanobj)
254	    {
255		/* Clean up object array */
256		o->object = NULL;
257		o->type = -1;
258		while (FcObjects[FcObjectsNumber-1].object == NULL)
259		    --FcObjectsNumber;
260	    }
261            break;
262	}
263    }
264}
265
266FcBool
267FcNameRegisterObjectTypes (const FcObjectType *types, int ntypes)
268{
269    int	i;
270
271    for (i = 0; i < ntypes; i++)
272	if (!FcObjectHashInsert (&types[i], FcTrue))
273	    return FcFalse;
274    return FcTrue;
275}
276
277FcBool
278FcNameUnregisterObjectTypes (const FcObjectType *types, int ntypes)
279{
280    int	i;
281
282    for (i = 0; i < ntypes; i++)
283	FcObjectHashRemove (&types[i], FcTrue);
284    return FcTrue;
285}
286
287const FcObjectType *
288FcNameGetObjectType (const char *object)
289{
290    return FcObjectFindByName (object, FcFalse);
291}
292
293FcBool
294FcObjectValidType (FcObject object, FcType type)
295{
296    FcObjectType    *t = FcObjectFindById (object);
297
298    if (t) {
299	switch (t->type) {
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 (t->type == -1 || type == t->type)
311		return FcTrue;
312	    break;
313	}
314	return FcFalse;
315    }
316    return FcTrue;
317}
318
319FcObject
320FcObjectFromName (const char * name)
321{
322    FcObjectType    *o = FcObjectFindByName (name, FcTrue);
323
324    if (o)
325	return FcObjectId (o);
326    return 0;
327}
328
329FcObjectSet *
330FcObjectGetSet (void)
331{
332    int		i;
333    FcObjectSet	*os = NULL;
334
335
336    os = FcObjectSetCreate ();
337    for (i = 0; i < FcObjectsNumber; i++)
338	FcObjectSetAdd (os, FcObjects[i].object);
339
340    return os;
341}
342
343FcBool
344FcObjectInit (void)
345{
346    int	i;
347
348    if (FcObjectsInited)
349	return FcTrue;
350
351    FcObjectsInited = FcTrue;
352    for (i = 0; i < NUM_OBJECT_TYPES; i++)
353	if (!FcObjectHashInsert (&_FcBaseObjectTypes[i], FcFalse))
354	    return FcFalse;
355    return FcTrue;
356}
357
358void
359FcObjectFini (void)
360{
361    int		    i;
362    FcObjectBucket  *b, *next;
363
364    for (i = 0; i < OBJECT_HASH_SIZE; i++)
365    {
366	for (b = FcObjectBuckets[i]; b; b = next)
367	{
368	    next = b->next;
369	    free (b);
370	}
371	FcObjectBuckets[i] = 0;
372    }
373    for (i = 0; i < FcObjectsNumber; i++)
374	if (FcObjects[i].type == (FcType)-1)
375	    free ((void*) FcObjects[i].object);
376    if (FcObjects != _FcBaseObjectTypes)
377	free (FcObjects);
378    FcObjects = (FcObjectType *) _FcBaseObjectTypes;
379    FcObjectsNumber = NUM_OBJECT_TYPES;
380    FcObjectsSize = 0;
381    FcObjectsInited = FcFalse;
382}
383
384const char *
385FcObjectName (FcObject object)
386{
387    FcObjectType    *o = FcObjectFindById (object);
388
389    if (o)
390	return o->object;
391    return NULL;
392}
393
394static const FcConstant _FcBaseConstants[] = {
395    { (FcChar8 *) "thin",	    "weight",   FC_WEIGHT_THIN, },
396    { (FcChar8 *) "extralight",	    "weight",   FC_WEIGHT_EXTRALIGHT, },
397    { (FcChar8 *) "ultralight",	    "weight",   FC_WEIGHT_EXTRALIGHT, },
398    { (FcChar8 *) "light",	    "weight",   FC_WEIGHT_LIGHT, },
399    { (FcChar8 *) "book",	    "weight",	FC_WEIGHT_BOOK, },
400    { (FcChar8 *) "regular",	    "weight",   FC_WEIGHT_REGULAR, },
401    { (FcChar8 *) "medium",	    "weight",   FC_WEIGHT_MEDIUM, },
402    { (FcChar8 *) "demibold",	    "weight",   FC_WEIGHT_DEMIBOLD, },
403    { (FcChar8 *) "semibold",	    "weight",   FC_WEIGHT_DEMIBOLD, },
404    { (FcChar8 *) "bold",	    "weight",   FC_WEIGHT_BOLD, },
405    { (FcChar8 *) "extrabold",	    "weight",   FC_WEIGHT_EXTRABOLD, },
406    { (FcChar8 *) "ultrabold",	    "weight",   FC_WEIGHT_EXTRABOLD, },
407    { (FcChar8 *) "black",	    "weight",   FC_WEIGHT_BLACK, },
408    { (FcChar8 *) "heavy",	    "weight",	FC_WEIGHT_HEAVY, },
409
410    { (FcChar8 *) "roman",	    "slant",    FC_SLANT_ROMAN, },
411    { (FcChar8 *) "italic",	    "slant",    FC_SLANT_ITALIC, },
412    { (FcChar8 *) "oblique",	    "slant",    FC_SLANT_OBLIQUE, },
413
414    { (FcChar8 *) "ultracondensed", "width",	FC_WIDTH_ULTRACONDENSED },
415    { (FcChar8 *) "extracondensed", "width",	FC_WIDTH_EXTRACONDENSED },
416    { (FcChar8 *) "condensed",	    "width",	FC_WIDTH_CONDENSED },
417    { (FcChar8 *) "semicondensed", "width",	FC_WIDTH_SEMICONDENSED },
418    { (FcChar8 *) "normal",	    "width",	FC_WIDTH_NORMAL },
419    { (FcChar8 *) "semiexpanded",   "width",	FC_WIDTH_SEMIEXPANDED },
420    { (FcChar8 *) "expanded",	    "width",	FC_WIDTH_EXPANDED },
421    { (FcChar8 *) "extraexpanded",  "width",	FC_WIDTH_EXTRAEXPANDED },
422    { (FcChar8 *) "ultraexpanded",  "width",	FC_WIDTH_ULTRAEXPANDED },
423
424    { (FcChar8 *) "proportional",   "spacing",  FC_PROPORTIONAL, },
425    { (FcChar8 *) "dual",	    "spacing",  FC_DUAL, },
426    { (FcChar8 *) "mono",	    "spacing",  FC_MONO, },
427    { (FcChar8 *) "charcell",	    "spacing",  FC_CHARCELL, },
428
429    { (FcChar8 *) "unknown",	    "rgba",	    FC_RGBA_UNKNOWN },
430    { (FcChar8 *) "rgb",	    "rgba",	    FC_RGBA_RGB, },
431    { (FcChar8 *) "bgr",	    "rgba",	    FC_RGBA_BGR, },
432    { (FcChar8 *) "vrgb",	    "rgba",	    FC_RGBA_VRGB },
433    { (FcChar8 *) "vbgr",	    "rgba",	    FC_RGBA_VBGR },
434    { (FcChar8 *) "none",	    "rgba",	    FC_RGBA_NONE },
435
436    { (FcChar8 *) "hintnone",	    "hintstyle",   FC_HINT_NONE },
437    { (FcChar8 *) "hintslight",	    "hintstyle",   FC_HINT_SLIGHT },
438    { (FcChar8 *) "hintmedium",	    "hintstyle",   FC_HINT_MEDIUM },
439    { (FcChar8 *) "hintfull",	    "hintstyle",   FC_HINT_FULL },
440
441    { (FcChar8 *) "antialias",	    "antialias",    FcTrue },
442    { (FcChar8 *) "hinting",	    "hinting",	    FcTrue },
443    { (FcChar8 *) "verticallayout", "verticallayout",	FcTrue },
444    { (FcChar8 *) "autohint",	    "autohint",	    FcTrue },
445    { (FcChar8 *) "globaladvance",  "globaladvance",	FcTrue }, /* deprecated */
446    { (FcChar8 *) "outline",	    "outline",	    FcTrue },
447    { (FcChar8 *) "scalable",	    "scalable",	    FcTrue },
448    { (FcChar8 *) "minspace",	    "minspace",	    FcTrue },
449    { (FcChar8 *) "embolden",	    "embolden",	    FcTrue },
450    { (FcChar8 *) "embeddedbitmap", "embeddedbitmap",	FcTrue },
451    { (FcChar8 *) "decorative",	    "decorative",   FcTrue },
452    { (FcChar8 *) "lcdnone",	    "lcdfilter",    FC_LCD_NONE },
453    { (FcChar8 *) "lcddefault",	    "lcdfilter",    FC_LCD_DEFAULT },
454    { (FcChar8 *) "lcdlight",	    "lcdfilter",    FC_LCD_LIGHT },
455    { (FcChar8 *) "lcdlegacy",	    "lcdfilter",    FC_LCD_LEGACY },
456};
457
458#define NUM_FC_CONSTANTS   (sizeof _FcBaseConstants/sizeof _FcBaseConstants[0])
459
460typedef struct _FcConstantList FcConstantList;
461
462struct _FcConstantList {
463    const FcConstantList    *next;
464    const FcConstant	    *consts;
465    int			    nconsts;
466};
467
468static const FcConstantList _FcBaseConstantList = {
469    0,
470    _FcBaseConstants,
471    NUM_FC_CONSTANTS
472};
473
474static const FcConstantList	*_FcConstants = &_FcBaseConstantList;
475
476FcBool
477FcNameRegisterConstants (const FcConstant *consts, int nconsts)
478{
479    FcConstantList	*l;
480
481    l = (FcConstantList *) malloc (sizeof (FcConstantList));
482    if (!l)
483	return FcFalse;
484    FcMemAlloc (FC_MEM_CONSTANT, sizeof (FcConstantList));
485    l->consts = consts;
486    l->nconsts = nconsts;
487    l->next = _FcConstants;
488    _FcConstants = l;
489    return FcTrue;
490}
491
492FcBool
493FcNameUnregisterConstants (const FcConstant *consts, int nconsts)
494{
495    const FcConstantList	*l, **prev;
496
497    for (prev = &_FcConstants;
498	 (l = *prev);
499	 prev = (const FcConstantList **) &(l->next))
500    {
501	if (l->consts == consts && l->nconsts == nconsts)
502	{
503	    *prev = l->next;
504	    FcMemFree (FC_MEM_CONSTANT, sizeof (FcConstantList));
505	    free ((void *) l);
506	    return FcTrue;
507	}
508    }
509    return FcFalse;
510}
511
512const FcConstant *
513FcNameGetConstant (const FcChar8 *string)
514{
515    const FcConstantList    *l;
516    int			    i;
517
518    for (l = _FcConstants; l; l = l->next)
519    {
520	for (i = 0; i < l->nconsts; i++)
521	    if (!FcStrCmpIgnoreCase (string, l->consts[i].name))
522		return &l->consts[i];
523    }
524    return 0;
525}
526
527FcBool
528FcNameConstant (const FcChar8 *string, int *result)
529{
530    const FcConstant	*c;
531
532    if ((c = FcNameGetConstant(string)))
533    {
534	*result = c->value;
535	return FcTrue;
536    }
537    return FcFalse;
538}
539
540FcBool
541FcNameBool (const FcChar8 *v, FcBool *result)
542{
543    char    c0, c1;
544
545    c0 = *v;
546    c0 = FcToLower (c0);
547    if (c0 == 't' || c0 == 'y' || c0 == '1')
548    {
549	*result = FcTrue;
550	return FcTrue;
551    }
552    if (c0 == 'f' || c0 == 'n' || c0 == '0')
553    {
554	*result = FcFalse;
555	return FcTrue;
556    }
557    if (c0 == 'o')
558    {
559	c1 = v[1];
560	c1 = FcToLower (c1);
561	if (c1 == 'n')
562	{
563	    *result = FcTrue;
564	    return FcTrue;
565	}
566	if (c1 == 'f')
567	{
568	    *result = FcFalse;
569	    return FcTrue;
570	}
571    }
572    return FcFalse;
573}
574
575static FcValue
576FcNameConvert (FcType type, FcChar8 *string)
577{
578    FcValue	v;
579    FcMatrix	m;
580
581    v.type = type;
582    switch (v.type) {
583    case FcTypeInteger:
584	if (!FcNameConstant (string, &v.u.i))
585	    v.u.i = atoi ((char *) string);
586	break;
587    case FcTypeString:
588	v.u.s = FcSharedStr (string);
589	if (!v.u.s)
590	    v.type = FcTypeVoid;
591	break;
592    case FcTypeBool:
593	if (!FcNameBool (string, &v.u.b))
594	    v.u.b = FcFalse;
595	break;
596    case FcTypeDouble:
597	v.u.d = strtod ((char *) string, 0);
598	break;
599    case FcTypeMatrix:
600	sscanf ((char *) string, "%lg %lg %lg %lg", &m.xx, &m.xy, &m.yx, &m.yy);
601	v.u.m = FcMatrixCopy (&m);
602	break;
603    case FcTypeCharSet:
604	v.u.c = FcNameParseCharSet (string);
605	if (!v.u.c)
606	    v.type = FcTypeVoid;
607	break;
608    case FcTypeLangSet:
609	v.u.l = FcNameParseLangSet (string);
610	if (!v.u.l)
611	    v.type = FcTypeVoid;
612	break;
613    default:
614	break;
615    }
616    return v;
617}
618
619static const FcChar8 *
620FcNameFindNext (const FcChar8 *cur, const char *delim, FcChar8 *save, FcChar8 *last)
621{
622    FcChar8    c;
623
624    while ((c = *cur))
625    {
626	if (c == '\\')
627	{
628	    ++cur;
629	    if (!(c = *cur))
630		break;
631	}
632	else if (strchr (delim, c))
633	    break;
634	++cur;
635	*save++ = c;
636    }
637    *save = 0;
638    *last = *cur;
639    if (*cur)
640	cur++;
641    return cur;
642}
643
644FcPattern *
645FcNameParse (const FcChar8 *name)
646{
647    FcChar8		*save;
648    FcPattern		*pat;
649    double		d;
650    FcChar8		*e;
651    FcChar8		delim;
652    FcValue		v;
653    const FcObjectType	*t;
654    const FcConstant	*c;
655
656    /* freed below */
657    save = malloc (strlen ((char *) name) + 1);
658    if (!save)
659	goto bail0;
660    pat = FcPatternCreate ();
661    if (!pat)
662	goto bail1;
663
664    for (;;)
665    {
666	name = FcNameFindNext (name, "-,:", save, &delim);
667	if (save[0])
668	{
669	    if (!FcPatternAddString (pat, FC_FAMILY, save))
670		goto bail2;
671	}
672	if (delim != ',')
673	    break;
674    }
675    if (delim == '-')
676    {
677	for (;;)
678	{
679	    name = FcNameFindNext (name, "-,:", save, &delim);
680	    d = strtod ((char *) save, (char **) &e);
681	    if (e != save)
682	    {
683		if (!FcPatternAddDouble (pat, FC_SIZE, d))
684		    goto bail2;
685	    }
686	    if (delim != ',')
687		break;
688	}
689    }
690    while (delim == ':')
691    {
692	name = FcNameFindNext (name, "=_:", save, &delim);
693	if (save[0])
694	{
695	    if (delim == '=' || delim == '_')
696	    {
697		t = FcNameGetObjectType ((char *) save);
698		for (;;)
699		{
700		    name = FcNameFindNext (name, ":,", save, &delim);
701		    if (t)
702		    {
703			v = FcNameConvert (t->type, save);
704			if (!FcPatternAdd (pat, t->object, v, FcTrue))
705			{
706			    FcValueDestroy (v);
707			    goto bail2;
708			}
709			FcValueDestroy (v);
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
741bail2:
742    FcPatternDestroy (pat);
743bail1:
744    free (save);
745bail0:
746    return 0;
747}
748static FcBool
749FcNameUnparseString (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
767FcBool
768FcNameUnparseValue (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
802FcBool
803FcNameUnparseValueList (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
821FcChar8 *
822FcNameUnparse (FcPattern *pat)
823{
824    return FcNameUnparseEscaped (pat, FcTrue);
825}
826
827FcChar8 *
828FcNameUnparseEscaped (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		continue;
860
861	    e = FcPatternObjectFindElt (pat, FcObjectFromName (o->object));
862	    if (e)
863	    {
864		if (!FcNameUnparseString (&buf, (FcChar8 *) ":", 0))
865		    goto bail0;
866		if (!FcNameUnparseString (&buf, (FcChar8 *) o->object, escape ? (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
867		    goto bail0;
868		if (!FcNameUnparseString (&buf, (FcChar8 *) "=", 0))
869		    goto bail0;
870		if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ?
871					     (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
872		    goto bail0;
873	    }
874	}
875    }
876    return FcStrBufDone (&buf);
877bail0:
878    FcStrBufDestroy (&buf);
879    return 0;
880}
881#define __fcname__
882#include "fcaliastail.h"
883#undef __fcname__
884