fcname.c revision 2eed41b2
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 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 * 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, },
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};
80
81#define NUM_OBJECT_TYPES    (sizeof _FcBaseObjectTypes / sizeof _FcBaseObjectTypes[0])
82
83typedef struct _FcObjectTypeList    FcObjectTypeList;
84
85struct _FcObjectTypeList {
86    const FcObjectTypeList  *next;
87    const FcObjectType	    *types;
88    int			    ntypes;
89};
90
91static const FcObjectTypeList _FcBaseObjectTypesList = {
92    0,
93    _FcBaseObjectTypes,
94    NUM_OBJECT_TYPES,
95};
96
97static const FcObjectTypeList	*_FcObjectTypes = &_FcBaseObjectTypesList;
98
99#define OBJECT_HASH_SIZE    31
100
101typedef struct _FcObjectBucket {
102    struct _FcObjectBucket  *next;
103    FcChar32		    hash;
104    FcObject		    id;
105} FcObjectBucket;
106
107static FcObjectBucket	*FcObjectBuckets[OBJECT_HASH_SIZE];
108
109static FcObjectType	*FcObjects = (FcObjectType *) _FcBaseObjectTypes;
110static int		FcObjectsNumber = NUM_OBJECT_TYPES;
111static int		FcObjectsSize = 0;
112static FcBool		FcObjectsInited;
113
114static FcObjectType *
115FcObjectInsert (const char *name, FcType type)
116{
117    FcObjectType    *o;
118    if (FcObjectsNumber >= FcObjectsSize)
119    {
120	int		newsize = FcObjectsNumber * 2;
121	FcObjectType	*newobjects;
122
123	if (FcObjectsSize)
124	    newobjects = realloc (FcObjects, newsize * sizeof (FcObjectType));
125	else
126	{
127	    newobjects = malloc (newsize * sizeof (FcObjectType));
128	    if (newobjects)
129		memcpy (newobjects, FcObjects,
130			FcObjectsNumber * sizeof (FcObjectType));
131	}
132	if (!newobjects)
133	    return NULL;
134	FcObjects = newobjects;
135	FcObjectsSize = newsize;
136    }
137    o = &FcObjects[FcObjectsNumber];
138    o->object = name;
139    o->type = type;
140    ++FcObjectsNumber;
141    return o;
142}
143
144static FcObject
145FcObjectId (FcObjectType *o)
146{
147    return o - FcObjects + 1;
148}
149
150static FcObjectType *
151FcObjectFindByName (const char *object, FcBool insert)
152{
153    FcChar32	    hash = FcStringHash ((const FcChar8 *) object);
154    FcObjectBucket  **p;
155    FcObjectBucket  *b;
156    FcObjectType    *o;
157
158    if (!FcObjectsInited)
159	FcObjectInit ();
160    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
161    {
162	o = FcObjects + b->id - 1;
163        if (b->hash == hash && !strcmp (object, (o->object)))
164            return o;
165    }
166    if (!insert)
167	return NULL;
168    /*
169     * Hook it into the hash chain
170     */
171    b = malloc (sizeof(FcObjectBucket));
172    if (!b)
173	return NULL;
174    object = (const char *) FcStrCopy ((FcChar8 *) object);
175    if (!object) {
176	free (b);
177	return NULL;
178    }
179    o = FcObjectInsert (object, -1);
180    b->next = NULL;
181    b->hash = hash;
182    b->id = FcObjectId (o);
183    *p = b;
184    return o;
185}
186
187static FcObjectType *
188FcObjectFindById (FcObject object)
189{
190    if (1 <= object && object <= FcObjectsNumber)
191	return FcObjects + object - 1;
192    return NULL;
193}
194
195static FcBool
196FcObjectHashInsert (const FcObjectType *object, FcBool copy)
197{
198    FcChar32	    hash = FcStringHash ((const FcChar8 *) object->object);
199    FcObjectBucket  **p;
200    FcObjectBucket  *b;
201    FcObjectType    *o;
202
203    if (!FcObjectsInited)
204	FcObjectInit ();
205    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
206    {
207	o = FcObjects + b->id - 1;
208        if (b->hash == hash && !strcmp (object->object, o->object))
209            return FcFalse;
210    }
211    /*
212     * Hook it into the hash chain
213     */
214    b = malloc (sizeof(FcObjectBucket));
215    if (!b)
216	return FcFalse;
217    if (copy)
218    {
219	o = FcObjectInsert (object->object, object->type);
220	if (!o)
221	{
222	    free (b);
223	    return FcFalse;
224	}
225    }
226    else
227	o = (FcObjectType *) object;
228    b->next = NULL;
229    b->hash = hash;
230    b->id = FcObjectId (o);
231    *p = b;
232    return FcTrue;
233}
234
235static void
236FcObjectHashRemove (const FcObjectType *object, FcBool cleanobj)
237{
238    FcChar32	    hash = FcStringHash ((const FcChar8 *) object->object);
239    FcObjectBucket  **p;
240    FcObjectBucket  *b;
241    FcObjectType    *o;
242
243    if (!FcObjectsInited)
244	FcObjectInit ();
245    for (p = &FcObjectBuckets[hash%OBJECT_HASH_SIZE]; (b = *p); p = &(b->next))
246    {
247	o = FcObjects + b->id - 1;
248        if (b->hash == hash && !strcmp (object->object, o->object))
249	{
250	    *p = b->next;
251	    free (b);
252	    if (cleanobj)
253	    {
254		/* Clean up object array */
255		o->object = NULL;
256		o->type = -1;
257		while (FcObjects[FcObjectsNumber-1].object == NULL)
258		    --FcObjectsNumber;
259	    }
260            break;
261	}
262    }
263}
264
265FcBool
266FcNameRegisterObjectTypes (const FcObjectType *types, int ntypes)
267{
268    int	i;
269
270    for (i = 0; i < ntypes; i++)
271	if (!FcObjectHashInsert (&types[i], FcTrue))
272	    return FcFalse;
273    return FcTrue;
274}
275
276FcBool
277FcNameUnregisterObjectTypes (const FcObjectType *types, int ntypes)
278{
279    int	i;
280
281    for (i = 0; i < ntypes; i++)
282	FcObjectHashRemove (&types[i], FcTrue);
283    return FcTrue;
284}
285
286const FcObjectType *
287FcNameGetObjectType (const char *object)
288{
289    return FcObjectFindByName (object, FcFalse);
290}
291
292FcBool
293FcObjectValidType (FcObject object, FcType type)
294{
295    FcObjectType    *t = FcObjectFindById (object);
296
297    if (t) {
298	switch (t->type) {
299	case -1:
300	    return FcTrue;
301	case FcTypeDouble:
302	case FcTypeInteger:
303	    if (type == FcTypeDouble || type == FcTypeInteger)
304		return FcTrue;
305	    break;
306	case FcTypeLangSet:
307	    if (type == FcTypeLangSet || type == FcTypeString)
308		return FcTrue;
309	    break;
310	default:
311	    if (type == t->type)
312		return FcTrue;
313	    break;
314	}
315	return FcFalse;
316    }
317    return FcTrue;
318}
319
320FcObject
321FcObjectFromName (const char * name)
322{
323    FcObjectType    *o = FcObjectFindByName (name, FcTrue);
324
325    if (o)
326	return FcObjectId (o);
327    return 0;
328}
329
330FcObjectSet *
331FcObjectGetSet (void)
332{
333    int		i;
334    FcObjectSet	*os = NULL;
335
336
337    os = FcObjectSetCreate ();
338    for (i = 0; i < FcObjectsNumber; i++)
339	FcObjectSetAdd (os, FcObjects[i].object);
340
341    return os;
342}
343
344FcBool
345FcObjectInit (void)
346{
347    int	i;
348
349    if (FcObjectsInited)
350	return FcTrue;
351
352    FcObjectsInited = FcTrue;
353    for (i = 0; i < NUM_OBJECT_TYPES; i++)
354	if (!FcObjectHashInsert (&_FcBaseObjectTypes[i], FcFalse))
355	    return FcFalse;
356    return FcTrue;
357}
358
359void
360FcObjectFini (void)
361{
362    int		    i;
363    FcObjectBucket  *b, *next;
364
365    for (i = 0; i < OBJECT_HASH_SIZE; i++)
366    {
367	for (b = FcObjectBuckets[i]; b; b = next)
368	{
369	    next = b->next;
370	    free (b);
371	}
372	FcObjectBuckets[i] = 0;
373    }
374    for (i = 0; i < FcObjectsNumber; i++)
375	if (FcObjects[i].type == (FcType)-1)
376	    free ((void*) FcObjects[i].object);
377    if (FcObjects != _FcBaseObjectTypes)
378	free (FcObjects);
379    FcObjects = (FcObjectType *) _FcBaseObjectTypes;
380    FcObjectsNumber = NUM_OBJECT_TYPES;
381    FcObjectsSize = 0;
382    FcObjectsInited = FcFalse;
383}
384
385const char *
386FcObjectName (FcObject object)
387{
388    FcObjectType    *o = FcObjectFindById (object);
389
390    if (o)
391	return o->object;
392    return NULL;
393}
394
395static const FcConstant _FcBaseConstants[] = {
396    { (FcChar8 *) "thin",	    "weight",   FC_WEIGHT_THIN, },
397    { (FcChar8 *) "extralight",	    "weight",   FC_WEIGHT_EXTRALIGHT, },
398    { (FcChar8 *) "ultralight",	    "weight",   FC_WEIGHT_EXTRALIGHT, },
399    { (FcChar8 *) "light",	    "weight",   FC_WEIGHT_LIGHT, },
400    { (FcChar8 *) "book",	    "weight",	FC_WEIGHT_BOOK, },
401    { (FcChar8 *) "regular",	    "weight",   FC_WEIGHT_REGULAR, },
402    { (FcChar8 *) "medium",	    "weight",   FC_WEIGHT_MEDIUM, },
403    { (FcChar8 *) "demibold",	    "weight",   FC_WEIGHT_DEMIBOLD, },
404    { (FcChar8 *) "semibold",	    "weight",   FC_WEIGHT_DEMIBOLD, },
405    { (FcChar8 *) "bold",	    "weight",   FC_WEIGHT_BOLD, },
406    { (FcChar8 *) "extrabold",	    "weight",   FC_WEIGHT_EXTRABOLD, },
407    { (FcChar8 *) "ultrabold",	    "weight",   FC_WEIGHT_EXTRABOLD, },
408    { (FcChar8 *) "black",	    "weight",   FC_WEIGHT_BLACK, },
409    { (FcChar8 *) "heavy",	    "weight",	FC_WEIGHT_HEAVY, },
410
411    { (FcChar8 *) "roman",	    "slant",    FC_SLANT_ROMAN, },
412    { (FcChar8 *) "italic",	    "slant",    FC_SLANT_ITALIC, },
413    { (FcChar8 *) "oblique",	    "slant",    FC_SLANT_OBLIQUE, },
414
415    { (FcChar8 *) "ultracondensed", "width",	FC_WIDTH_ULTRACONDENSED },
416    { (FcChar8 *) "extracondensed", "width",	FC_WIDTH_EXTRACONDENSED },
417    { (FcChar8 *) "condensed",	    "width",	FC_WIDTH_CONDENSED },
418    { (FcChar8 *) "semicondensed", "width",	FC_WIDTH_SEMICONDENSED },
419    { (FcChar8 *) "normal",	    "width",	FC_WIDTH_NORMAL },
420    { (FcChar8 *) "semiexpanded",   "width",	FC_WIDTH_SEMIEXPANDED },
421    { (FcChar8 *) "expanded",	    "width",	FC_WIDTH_EXPANDED },
422    { (FcChar8 *) "extraexpanded",  "width",	FC_WIDTH_EXTRAEXPANDED },
423    { (FcChar8 *) "ultraexpanded",  "width",	FC_WIDTH_ULTRAEXPANDED },
424
425    { (FcChar8 *) "proportional",   "spacing",  FC_PROPORTIONAL, },
426    { (FcChar8 *) "dual",	    "spacing",  FC_DUAL, },
427    { (FcChar8 *) "mono",	    "spacing",  FC_MONO, },
428    { (FcChar8 *) "charcell",	    "spacing",  FC_CHARCELL, },
429
430    { (FcChar8 *) "unknown",	    "rgba",	    FC_RGBA_UNKNOWN },
431    { (FcChar8 *) "rgb",	    "rgba",	    FC_RGBA_RGB, },
432    { (FcChar8 *) "bgr",	    "rgba",	    FC_RGBA_BGR, },
433    { (FcChar8 *) "vrgb",	    "rgba",	    FC_RGBA_VRGB },
434    { (FcChar8 *) "vbgr",	    "rgba",	    FC_RGBA_VBGR },
435    { (FcChar8 *) "none",	    "rgba",	    FC_RGBA_NONE },
436
437    { (FcChar8 *) "hintnone",	    "hintstyle",   FC_HINT_NONE },
438    { (FcChar8 *) "hintslight",	    "hintstyle",   FC_HINT_SLIGHT },
439    { (FcChar8 *) "hintmedium",	    "hintstyle",   FC_HINT_MEDIUM },
440    { (FcChar8 *) "hintfull",	    "hintstyle",   FC_HINT_FULL },
441
442    { (FcChar8 *) "antialias",	    "antialias",    FcTrue },
443    { (FcChar8 *) "hinting",	    "hinting",	    FcTrue },
444    { (FcChar8 *) "verticallayout", "verticallayout",	FcTrue },
445    { (FcChar8 *) "autohint",	    "autohint",	    FcTrue },
446    { (FcChar8 *) "globaladvance",  "globaladvance",	FcTrue },
447    { (FcChar8 *) "outline",	    "outline",	    FcTrue },
448    { (FcChar8 *) "scalable",	    "scalable",	    FcTrue },
449    { (FcChar8 *) "minspace",	    "minspace",	    FcTrue },
450    { (FcChar8 *) "embolden",	    "embolden",	    FcTrue },
451    { (FcChar8 *) "embeddedbitmap", "embeddedbitmap",	FcTrue },
452    { (FcChar8 *) "decorative",	    "decorative",   FcTrue },
453    { (FcChar8 *) "lcdnone",	    "lcdfilter",    FC_LCD_NONE },
454    { (FcChar8 *) "lcddefault",	    "lcdfilter",    FC_LCD_DEFAULT },
455    { (FcChar8 *) "lcdlight",	    "lcdfilter",    FC_LCD_LIGHT },
456    { (FcChar8 *) "lcdlegacy",	    "lcdfilter",    FC_LCD_LEGACY },
457};
458
459#define NUM_FC_CONSTANTS   (sizeof _FcBaseConstants/sizeof _FcBaseConstants[0])
460
461typedef struct _FcConstantList FcConstantList;
462
463struct _FcConstantList {
464    const FcConstantList    *next;
465    const FcConstant	    *consts;
466    int			    nconsts;
467};
468
469static const FcConstantList _FcBaseConstantList = {
470    0,
471    _FcBaseConstants,
472    NUM_FC_CONSTANTS
473};
474
475static const FcConstantList	*_FcConstants = &_FcBaseConstantList;
476
477FcBool
478FcNameRegisterConstants (const FcConstant *consts, int nconsts)
479{
480    FcConstantList	*l;
481
482    l = (FcConstantList *) malloc (sizeof (FcConstantList));
483    if (!l)
484	return FcFalse;
485    FcMemAlloc (FC_MEM_CONSTANT, sizeof (FcConstantList));
486    l->consts = consts;
487    l->nconsts = nconsts;
488    l->next = _FcConstants;
489    _FcConstants = l;
490    return FcTrue;
491}
492
493FcBool
494FcNameUnregisterConstants (const FcConstant *consts, int nconsts)
495{
496    const FcConstantList	*l, **prev;
497
498    for (prev = &_FcConstants;
499	 (l = *prev);
500	 prev = (const FcConstantList **) &(l->next))
501    {
502	if (l->consts == consts && l->nconsts == nconsts)
503	{
504	    *prev = l->next;
505	    FcMemFree (FC_MEM_CONSTANT, sizeof (FcConstantList));
506	    free ((void *) l);
507	    return FcTrue;
508	}
509    }
510    return FcFalse;
511}
512
513const FcConstant *
514FcNameGetConstant (FcChar8 *string)
515{
516    const FcConstantList    *l;
517    int			    i;
518
519    for (l = _FcConstants; l; l = l->next)
520    {
521	for (i = 0; i < l->nconsts; i++)
522	    if (!FcStrCmpIgnoreCase (string, l->consts[i].name))
523		return &l->consts[i];
524    }
525    return 0;
526}
527
528FcBool
529FcNameConstant (FcChar8 *string, int *result)
530{
531    const FcConstant	*c;
532
533    if ((c = FcNameGetConstant(string)))
534    {
535	*result = c->value;
536	return FcTrue;
537    }
538    return FcFalse;
539}
540
541FcBool
542FcNameBool (const FcChar8 *v, FcBool *result)
543{
544    char    c0, c1;
545
546    c0 = *v;
547    c0 = FcToLower (c0);
548    if (c0 == 't' || c0 == 'y' || c0 == '1')
549    {
550	*result = FcTrue;
551	return FcTrue;
552    }
553    if (c0 == 'f' || c0 == 'n' || c0 == '0')
554    {
555	*result = FcFalse;
556	return FcTrue;
557    }
558    if (c0 == 'o')
559    {
560	c1 = v[1];
561	c1 = FcToLower (c1);
562	if (c1 == 'n')
563	{
564	    *result = FcTrue;
565	    return FcTrue;
566	}
567	if (c1 == 'f')
568	{
569	    *result = FcFalse;
570	    return FcTrue;
571	}
572    }
573    return FcFalse;
574}
575
576static FcValue
577FcNameConvert (FcType type, FcChar8 *string, FcMatrix *m)
578{
579    FcValue	v;
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 = FcStrStaticName(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	v.u.m = m;
601	sscanf ((char *) string, "%lg %lg %lg %lg", &m->xx, &m->xy, &m->yx, &m->yy);
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    FcMatrix		m;
654    const FcObjectType	*t;
655    const FcConstant	*c;
656
657    /* freed below */
658    save = malloc (strlen ((char *) name) + 1);
659    if (!save)
660	goto bail0;
661    pat = FcPatternCreate ();
662    if (!pat)
663	goto bail1;
664
665    for (;;)
666    {
667	name = FcNameFindNext (name, "-,:", save, &delim);
668	if (save[0])
669	{
670	    if (!FcPatternAddString (pat, FC_FAMILY, save))
671		goto bail2;
672	}
673	if (delim != ',')
674	    break;
675    }
676    if (delim == '-')
677    {
678	for (;;)
679	{
680	    name = FcNameFindNext (name, "-,:", save, &delim);
681	    d = strtod ((char *) save, (char **) &e);
682	    if (e != save)
683	    {
684		if (!FcPatternAddDouble (pat, FC_SIZE, d))
685		    goto bail2;
686	    }
687	    if (delim != ',')
688		break;
689	}
690    }
691    while (delim == ':')
692    {
693	name = FcNameFindNext (name, "=_:", save, &delim);
694	if (save[0])
695	{
696	    if (delim == '=' || delim == '_')
697	    {
698		t = FcNameGetObjectType ((char *) save);
699		for (;;)
700		{
701		    name = FcNameFindNext (name, ":,", save, &delim);
702		    if (t)
703		    {
704			v = FcNameConvert (t->type, save, &m);
705			if (!FcPatternAdd (pat, t->object, v, FcTrue))
706			{
707			    switch (v.type) {
708			    case FcTypeCharSet:
709				FcCharSetDestroy ((FcCharSet *) v.u.c);
710				break;
711			    case FcTypeLangSet:
712				FcLangSetDestroy ((FcLangSet *) v.u.l);
713				break;
714			    default:
715				break;
716			    }
717			    goto bail2;
718			}
719			switch (v.type) {
720			case FcTypeCharSet:
721			    FcCharSetDestroy ((FcCharSet *) v.u.c);
722			    break;
723			case FcTypeLangSet:
724			    FcLangSetDestroy ((FcLangSet *) v.u.l);
725			    break;
726			default:
727			    break;
728			}
729		    }
730		    if (delim != ',')
731			break;
732		}
733	    }
734	    else
735	    {
736		if ((c = FcNameGetConstant (save)))
737		{
738		    t = FcNameGetObjectType ((char *) c->object);
739		    switch (t->type) {
740		    case FcTypeInteger:
741		    case FcTypeDouble:
742			if (!FcPatternAddInteger (pat, c->object, c->value))
743			    goto bail2;
744			break;
745		    case FcTypeBool:
746			if (!FcPatternAddBool (pat, c->object, c->value))
747			    goto bail2;
748			break;
749		    default:
750			break;
751		    }
752		}
753	    }
754	}
755    }
756
757    free (save);
758    return pat;
759
760bail2:
761    FcPatternDestroy (pat);
762bail1:
763    free (save);
764bail0:
765    return 0;
766}
767static FcBool
768FcNameUnparseString (FcStrBuf	    *buf,
769		     const FcChar8  *string,
770		     const FcChar8  *escape)
771{
772    FcChar8 c;
773    while ((c = *string++))
774    {
775	if (escape && strchr ((char *) escape, (char) c))
776	{
777	    if (!FcStrBufChar (buf, escape[0]))
778		return FcFalse;
779	}
780	if (!FcStrBufChar (buf, c))
781	    return FcFalse;
782    }
783    return FcTrue;
784}
785
786FcBool
787FcNameUnparseValue (FcStrBuf	*buf,
788		    FcValue	*v0,
789		    FcChar8	*escape)
790{
791    FcChar8	temp[1024];
792    FcValue v = FcValueCanonicalize(v0);
793
794    switch (v.type) {
795    case FcTypeVoid:
796	return FcTrue;
797    case FcTypeInteger:
798	sprintf ((char *) temp, "%d", v.u.i);
799	return FcNameUnparseString (buf, temp, 0);
800    case FcTypeDouble:
801	sprintf ((char *) temp, "%g", v.u.d);
802	return FcNameUnparseString (buf, temp, 0);
803    case FcTypeString:
804	return FcNameUnparseString (buf, v.u.s, escape);
805    case FcTypeBool:
806	return FcNameUnparseString (buf, v.u.b ? (FcChar8 *) "True" : (FcChar8 *) "False", 0);
807    case FcTypeMatrix:
808	sprintf ((char *) temp, "%g %g %g %g",
809		 v.u.m->xx, v.u.m->xy, v.u.m->yx, v.u.m->yy);
810	return FcNameUnparseString (buf, temp, 0);
811    case FcTypeCharSet:
812	return FcNameUnparseCharSet (buf, v.u.c);
813    case FcTypeLangSet:
814	return FcNameUnparseLangSet (buf, v.u.l);
815    case FcTypeFTFace:
816	return FcTrue;
817    }
818    return FcFalse;
819}
820
821FcBool
822FcNameUnparseValueList (FcStrBuf	*buf,
823			FcValueListPtr	v,
824			FcChar8		*escape)
825{
826    while (v)
827    {
828	if (!FcNameUnparseValue (buf, &v->value, escape))
829	    return FcFalse;
830	if ((v = FcValueListNext(v)) != NULL)
831	    if (!FcNameUnparseString (buf, (FcChar8 *) ",", 0))
832		return FcFalse;
833    }
834    return FcTrue;
835}
836
837#define FC_ESCAPE_FIXED    "\\-:,"
838#define FC_ESCAPE_VARIABLE "\\=_:,"
839
840FcChar8 *
841FcNameUnparse (FcPattern *pat)
842{
843    return FcNameUnparseEscaped (pat, FcTrue);
844}
845
846FcChar8 *
847FcNameUnparseEscaped (FcPattern *pat, FcBool escape)
848{
849    FcStrBuf		    buf;
850    FcChar8		    buf_static[8192];
851    int			    i;
852    FcPatternElt	    *e;
853    const FcObjectTypeList  *l;
854    const FcObjectType	    *o;
855
856    FcStrBufInit (&buf, buf_static, sizeof (buf_static));
857    e = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT);
858    if (e)
859    {
860        if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ? (FcChar8 *) FC_ESCAPE_FIXED : 0))
861	    goto bail0;
862    }
863    e = FcPatternObjectFindElt (pat, FC_SIZE_OBJECT);
864    if (e)
865    {
866	if (!FcNameUnparseString (&buf, (FcChar8 *) "-", 0))
867	    goto bail0;
868	if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ? (FcChar8 *) FC_ESCAPE_FIXED : 0))
869	    goto bail0;
870    }
871    for (l = _FcObjectTypes; l; l = l->next)
872    {
873	for (i = 0; i < l->ntypes; i++)
874	{
875	    o = &l->types[i];
876	    if (!strcmp (o->object, FC_FAMILY) ||
877		!strcmp (o->object, FC_SIZE) ||
878		!strcmp (o->object, FC_FILE))
879		continue;
880
881	    e = FcPatternObjectFindElt (pat, FcObjectFromName (o->object));
882	    if (e)
883	    {
884		if (!FcNameUnparseString (&buf, (FcChar8 *) ":", 0))
885		    goto bail0;
886		if (!FcNameUnparseString (&buf, (FcChar8 *) o->object, escape ? (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
887		    goto bail0;
888		if (!FcNameUnparseString (&buf, (FcChar8 *) "=", 0))
889		    goto bail0;
890		if (!FcNameUnparseValueList (&buf, FcPatternEltValues(e), escape ?
891					     (FcChar8 *) FC_ESCAPE_VARIABLE : 0))
892		    goto bail0;
893	    }
894	}
895    }
896    return FcStrBufDone (&buf);
897bail0:
898    FcStrBufDestroy (&buf);
899    return 0;
900}
901#define __fcname__
902#include "fcaliastail.h"
903#undef __fcname__
904