fontxlfd.c revision 23a0898a
1/* $Xorg: fontxlfd.c,v 1.4 2001/02/09 02:04:04 xorgcvs Exp $ */
2
3/*
4
5Copyright 1990, 1998  The Open Group
6
7Permission to use, copy, modify, distribute, and sell this software and its
8documentation for any purpose is hereby granted without fee, provided that
9the above copyright notice appear in all copies and that both that
10copyright notice and this permission notice appear in supporting
11documentation.
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of The Open Group shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from The Open Group.
28
29*/
30/* $XFree86: xc/lib/font/util/fontxlfd.c,v 3.16tsi Exp $ */
31
32/*
33 * Author:  Keith Packard, MIT X Consortium
34 */
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39#include	<X11/fonts/fontmisc.h>
40#include	<X11/fonts/fontstruct.h>
41#include	<X11/fonts/fontxlfd.h>
42#include	<X11/fonts/fontutil.h>
43#include	<X11/Xos.h>
44#include	<math.h>
45#include	<stdlib.h>
46#if defined(sony) && !defined(SYSTYPE_SYSV) && !defined(_SYSTYPE_SYSV)
47#define NO_LOCALE
48#endif
49#ifndef NO_LOCALE
50#include	<locale.h>
51#endif
52#include	<ctype.h>
53#include	<stdio.h>	/* for sprintf() */
54
55static char *
56GetInt(char *ptr, int *val)
57{
58    if (*ptr == '*') {
59	*val = -1;
60	ptr++;
61    } else
62	for (*val = 0; *ptr >= '0' && *ptr <= '9';)
63	    *val = *val * 10 + *ptr++ - '0';
64    if (*ptr == '-')
65	return ptr;
66    return (char *) 0;
67}
68
69#define minchar(p) ((p).min_char_low + ((p).min_char_high << 8))
70#define maxchar(p) ((p).max_char_low + ((p).max_char_high << 8))
71
72
73#ifndef NO_LOCALE
74static struct lconv *locale = 0;
75#endif
76static char *radix = ".", *plus = "+", *minus = "-";
77
78static char *
79readreal(char *ptr, double *result)
80{
81    char buffer[80], *p1, *p2;
82
83#ifndef NO_LOCALE
84    /* Figure out what symbols apply in this locale */
85
86    if (!locale)
87    {
88	locale = localeconv();
89	if (locale->decimal_point && *locale->decimal_point)
90	    radix = locale->decimal_point;
91	if (locale->positive_sign && *locale->positive_sign)
92	    plus = locale->positive_sign;
93	if (locale->negative_sign && *locale->negative_sign)
94	    minus = locale->negative_sign;
95    }
96#endif
97    /* Copy the first 80 chars of ptr into our local buffer, changing
98       symbols as needed. */
99    for (p1 = ptr, p2 = buffer;
100	 *p1 && (p2 - buffer) < sizeof(buffer) - 1;
101	 p1++, p2++)
102    {
103	switch(*p1)
104	{
105	    case '~': *p2 = *minus; break;
106	    case '+': *p2 = *plus; break;
107	    case '.': *p2 = *radix; break;
108	    default: *p2 = *p1;
109	}
110    }
111    *p2 = 0;
112
113    /* Now we have something that strtod() can interpret... do it. */
114    *result = strtod(buffer, &p1);
115    /* Return NULL if failure, pointer past number if success */
116    return (p1 == buffer) ? (char *)0 : (ptr + (p1 - buffer));
117}
118
119static char *
120xlfd_double_to_text(double value, char *buffer, int space_required)
121{
122    char formatbuf[40];
123    register char *p1;
124    int ndigits, exponent;
125
126#ifndef NO_LOCALE
127    if (!locale)
128    {
129	locale = localeconv();
130	if (locale->decimal_point && *locale->decimal_point)
131	    radix = locale->decimal_point;
132	if (locale->positive_sign && *locale->positive_sign)
133	    plus = locale->positive_sign;
134	if (locale->negative_sign && *locale->negative_sign)
135	    minus = locale->negative_sign;
136    }
137#endif
138    /* Compute a format to use to render the number */
139    sprintf(formatbuf, "%%.%dle", XLFD_NDIGITS);
140
141    if (space_required)
142	*buffer++ = ' ';
143
144    /* Render the number using printf's idea of formatting */
145    sprintf(buffer, formatbuf, value);
146
147    /* Find and read the exponent value */
148    for (p1 = buffer + strlen(buffer);
149	*p1-- != 'e' && p1[1] != 'E';);
150    exponent = atoi(p1 + 2);
151    if (value == 0.0) exponent = 0;
152
153    /* Figure out how many digits are significant */
154    while (p1 >= buffer && (!isdigit(*p1) || *p1 == '0')) p1--;
155    ndigits = 0;
156    while (p1 >= buffer) if (isdigit(*p1--)) ndigits++;
157
158    /* Figure out notation to use */
159    if (exponent >= XLFD_NDIGITS || ndigits - exponent > XLFD_NDIGITS + 1)
160    {
161	/* Scientific */
162	sprintf(formatbuf, "%%.%dle", ndigits - 1);
163	sprintf(buffer, formatbuf, value);
164    }
165    else
166    {
167	/* Fixed */
168	ndigits -= exponent + 1;
169	if (ndigits < 0) ndigits = 0;
170	sprintf(formatbuf, "%%.%dlf", ndigits);
171	sprintf(buffer, formatbuf, value);
172	if (exponent < 0)
173	{
174	    p1 = buffer;
175	    while (*p1 && *p1 != '0') p1++;
176	    while (*p1++) p1[-1] = *p1;
177	}
178    }
179
180    /* Last step, convert the locale-specific sign and radix characters
181       to our own. */
182    for (p1 = buffer; *p1; p1++)
183    {
184	if (*p1 == *minus) *p1 = '~';
185	else if (*p1 == *plus) *p1 = '+';
186	else if (*p1 == *radix) *p1 = '.';
187    }
188
189    return buffer - space_required;
190}
191
192double
193xlfd_round_double(double x)
194{
195   /* Utility for XLFD users to round numbers to XLFD_NDIGITS
196      significant digits.  How do you round to n significant digits on
197      a binary machine?  */
198
199#if defined(i386) || defined(__i386__) || \
200    defined(ia64) || defined(__ia64__) || \
201    defined(__alpha__) || defined(__alpha) || \
202    defined(__hppa__) || \
203    defined(__amd64__) || defined(__amd64) || \
204    defined(sgi)
205#if !defined(__UNIXOS2__)
206#include <float.h>
207
208/* if we have IEEE 754 fp, we can round to binary digits... */
209
210#if (FLT_RADIX == 2) && (DBL_DIG == 15) && (DBL_MANT_DIG == 53)
211
212#ifndef M_LN2
213#define M_LN2       0.69314718055994530942
214#endif
215#ifndef M_LN10
216#define M_LN10      2.30258509299404568402
217#endif
218
219/* convert # of decimal digits to # of binary digits */
220#define XLFD_NDIGITS_2 ((int)(XLFD_NDIGITS * M_LN10 / M_LN2 + 0.5))
221
222   union conv_d {
223      double d;
224      unsigned char b[8];
225   } d;
226   int i,j,k,d_exp;
227
228   if (x == 0)
229      return x;
230
231   /* do minor sanity check for IEEE 754 fp and correct byte order */
232   d.d = 1.0;
233   if (sizeof(double) == 8 && d.b[7] == 0x3f && d.b[6] == 0xf0) {
234
235      /*
236       * this code will round IEEE 754 double to XLFD_NDIGITS_2 binary digits
237       */
238
239      d.d = x;
240      d_exp = (d.b[7] << 4) | (d.b[6] >> 4);
241
242      i = (DBL_MANT_DIG-XLFD_NDIGITS_2) >> 3;
243      j = 1 << ((DBL_MANT_DIG-XLFD_NDIGITS_2) & 0x07);
244      for (; i<7; i++) {
245	 k = d.b[i] + j;
246	 d.b[i] = k;
247	 if (k & 0x100) j = 1;
248	 else break;
249      }
250      if ((i==7) && ((d.b[6] & 0xf0) != ((d_exp<<4) & 0xf0))) {
251	 /* mantissa overflow: increment exponent */
252	 d_exp = (d_exp & 0x800 ) | ((d_exp & 0x7ff) + 1);
253	 d.b[7] = d_exp >> 4;
254	 d.b[6] = (d.b[6] & 0x0f) | (d_exp << 4);
255      }
256
257      i = (DBL_MANT_DIG-XLFD_NDIGITS_2) >> 3;
258      j = 1 << ((DBL_MANT_DIG-XLFD_NDIGITS_2) & 0x07);
259      d.b[i] &= ~(j-1);
260      for (;--i>=0;) d.b[i] = 0;
261
262      return d.d;
263   }
264   else
265#endif
266#endif /* !__UNIXOS2__ */
267#endif /* i386 || __i386__ */
268    {
269	/*
270	 * If not IEEE 754:  Let printf() do it for you.
271	 */
272
273	char formatbuf[40], buffer[40];
274
275	sprintf(formatbuf, "%%.%dlg", XLFD_NDIGITS);
276	sprintf(buffer, formatbuf, x);
277	return atof(buffer);
278    }
279}
280
281static char *
282GetMatrix(char *ptr, FontScalablePtr vals, int which)
283{
284    double *matrix;
285
286    if (which == PIXELSIZE_MASK)
287	matrix = vals->pixel_matrix;
288    else if (which == POINTSIZE_MASK)
289	matrix = vals->point_matrix;
290    else return (char *)0;
291
292    while (isspace(*ptr)) ptr++;
293    if (*ptr == '[')
294    {
295	/* This is a matrix containing real numbers.  It would be nice
296	   to use strtod() or sscanf() to read the numbers, but those
297	   don't handle '~' for minus and we cannot force them to use a
298	   "."  for the radix.  We'll have to do the hard work ourselves
299	   (in readreal()).  */
300
301	if ((ptr = readreal(++ptr, matrix + 0)) &&
302	    (ptr = readreal(ptr, matrix + 1)) &&
303	    (ptr = readreal(ptr, matrix + 2)) &&
304	    (ptr = readreal(ptr, matrix + 3)))
305	{
306	    while (isspace(*ptr)) ptr++;
307	    if (*ptr != ']')
308		ptr = (char *)0;
309	    else
310	    {
311		ptr++;
312		while (isspace(*ptr)) ptr++;
313		if (*ptr == '-')
314		{
315		    if (which == POINTSIZE_MASK)
316			vals->values_supplied |= POINTSIZE_ARRAY;
317		    else
318			vals->values_supplied |= PIXELSIZE_ARRAY;
319		}
320		else ptr = (char *)0;
321	    }
322	}
323    }
324    else
325    {
326	int value;
327	if ((ptr = GetInt(ptr, &value)))
328	{
329	    vals->values_supplied &= ~which;
330	    if (value > 0)
331	    {
332		matrix[3] = (double)value;
333		if (which == POINTSIZE_MASK)
334		{
335		    matrix[3] /= 10.0;
336		    vals->values_supplied |= POINTSIZE_SCALAR;
337		}
338		else
339		    vals->values_supplied |= PIXELSIZE_SCALAR;
340		/* If we're concocting the pixelsize array from a scalar,
341		   we will need to normalize element 0 for the pixel shape.
342		   This is done in FontFileCompleteXLFD(). */
343		matrix[0] = matrix[3];
344		matrix[1] = matrix[2] = 0.0;
345	    }
346	    else if (value < 0)
347	    {
348		if (which == POINTSIZE_MASK)
349		    vals->values_supplied |= POINTSIZE_WILDCARD;
350		else
351		    vals->values_supplied |= PIXELSIZE_WILDCARD;
352	    }
353	}
354    }
355    return ptr;
356}
357
358
359static void
360append_ranges(char *fname, int nranges, fsRange *ranges)
361{
362    if (nranges)
363    {
364        int i;
365
366        strcat(fname, "[");
367        for (i = 0; i < nranges && strlen(fname) < 1010; i++)
368        {
369	    if (i) strcat(fname, " ");
370	    sprintf(fname + strlen(fname), "%d",
371		    minchar(ranges[i]));
372	    if (ranges[i].min_char_low ==
373		ranges[i].max_char_low &&
374		ranges[i].min_char_high ==
375		ranges[i].max_char_high) continue;
376	    sprintf(fname + strlen(fname), "_%d",
377		    maxchar(ranges[i]));
378        }
379        strcat(fname, "]");
380    }
381}
382
383Bool
384FontParseXLFDName(char *fname, FontScalablePtr vals, int subst)
385{
386    register char *ptr;
387    register char *ptr1,
388               *ptr2,
389               *ptr3,
390               *ptr4;
391    register char *ptr5;
392    FontScalableRec tmpvals;
393    char        replaceChar = '0';
394    char        tmpBuf[1024];
395    int         spacingLen;
396    int		l;
397    char	*p;
398
399    bzero(&tmpvals, sizeof(tmpvals));
400    if (subst != FONT_XLFD_REPLACE_VALUE)
401	*vals = tmpvals;
402
403    if (!(*(ptr = fname) == '-' || (*ptr++ == '*' && *ptr == '-')) ||  /* fndry */
404	    !(ptr = strchr(ptr + 1, '-')) ||	/* family_name */
405	    !(ptr1 = ptr = strchr(ptr + 1, '-')) ||	/* weight_name */
406	    !(ptr = strchr(ptr + 1, '-')) ||	/* slant */
407	    !(ptr = strchr(ptr + 1, '-')) ||	/* setwidth_name */
408	    !(ptr = strchr(ptr + 1, '-')) ||	/* add_style_name */
409	    !(ptr = strchr(ptr + 1, '-')) ||	/* pixel_size */
410	    !(ptr = GetMatrix(ptr + 1, &tmpvals, PIXELSIZE_MASK)) ||
411	    !(ptr2 = ptr = GetMatrix(ptr + 1, &tmpvals, POINTSIZE_MASK)) ||
412	    !(ptr = GetInt(ptr + 1, &tmpvals.x)) ||	/* resolution_x */
413	    !(ptr3 = ptr = GetInt(ptr + 1, &tmpvals.y)) ||  /* resolution_y */
414	    !(ptr4 = ptr = strchr(ptr + 1, '-')) ||	/* spacing */
415	    !(ptr5 = ptr = GetInt(ptr + 1, &tmpvals.width)) || /* average_width */
416	    !(ptr = strchr(ptr + 1, '-')) ||	/* charset_registry */
417	    strchr(ptr + 1, '-'))/* charset_encoding */
418	return FALSE;
419
420    /* Lop off HP charset subsetting enhancement.  Interpreting this
421       field requires allocating some space in which to return the
422       results.  So, to prevent memory leaks, this procedure will simply
423       lop off and ignore charset subsetting, and initialize the
424       relevant vals fields to zero.  It's up to the caller to make its
425       own call to FontParseRanges() if it's interested in the charset
426       subsetting.  */
427
428    if (subst != FONT_XLFD_REPLACE_NONE &&
429	(p = strchr(strrchr(fname, '-'), '[')))
430    {
431	tmpvals.values_supplied |= CHARSUBSET_SPECIFIED;
432	*p = '\0';
433    }
434
435    /* Fill in deprecated fields for the benefit of rasterizers that care
436       about them. */
437    tmpvals.pixel = (tmpvals.pixel_matrix[3] >= 0) ?
438		    (int)(tmpvals.pixel_matrix[3] + .5) :
439		    (int)(tmpvals.pixel_matrix[3] - .5);
440    tmpvals.point = (tmpvals.point_matrix[3] >= 0) ?
441                    (int)(tmpvals.point_matrix[3] * 10 + .5) :
442                    (int)(tmpvals.point_matrix[3] * 10 - .5);
443
444    spacingLen = ptr4 - ptr3 + 1;
445
446    switch (subst) {
447    case FONT_XLFD_REPLACE_NONE:
448	*vals = tmpvals;
449	break;
450    case FONT_XLFD_REPLACE_STAR:
451	replaceChar = '*';
452    case FONT_XLFD_REPLACE_ZERO:
453	strcpy(tmpBuf, ptr2);
454	ptr5 = tmpBuf + (ptr5 - ptr2);
455	ptr3 = tmpBuf + (ptr3 - ptr2);
456	ptr2 = tmpBuf;
457	ptr = ptr1 + 1;
458
459	ptr = strchr(ptr, '-') + 1;		/* skip weight */
460	ptr = strchr(ptr, '-') + 1;		/* skip slant */
461	ptr = strchr(ptr, '-') + 1;		/* skip setwidth_name */
462	ptr = strchr(ptr, '-') + 1;		/* skip add_style_name */
463
464	if ((ptr - fname) + spacingLen + strlen(ptr5) + 10 >= (unsigned)1024)
465	    return FALSE;
466	*ptr++ = replaceChar;
467	*ptr++ = '-';
468	*ptr++ = replaceChar;
469	*ptr++ = '-';
470	*ptr++ = '*';
471	*ptr++ = '-';
472	*ptr++ = '*';
473	if (spacingLen > 2)
474	{
475	    memmove(ptr, ptr3, spacingLen);
476	    ptr += spacingLen;
477	}
478	else
479	{
480	    *ptr++ = '-';
481	    *ptr++ = '*';
482	    *ptr++ = '-';
483	}
484	*ptr++ = replaceChar;
485	strcpy(ptr, ptr5);
486	*vals = tmpvals;
487	break;
488    case FONT_XLFD_REPLACE_VALUE:
489	if (vals->values_supplied & PIXELSIZE_MASK)
490	{
491	    tmpvals.values_supplied =
492		(tmpvals.values_supplied & ~PIXELSIZE_MASK) |
493		(vals->values_supplied & PIXELSIZE_MASK);
494	    tmpvals.pixel_matrix[0] = vals->pixel_matrix[0];
495	    tmpvals.pixel_matrix[1] = vals->pixel_matrix[1];
496	    tmpvals.pixel_matrix[2] = vals->pixel_matrix[2];
497	    tmpvals.pixel_matrix[3] = vals->pixel_matrix[3];
498	}
499	if (vals->values_supplied & POINTSIZE_MASK)
500	{
501	    tmpvals.values_supplied =
502		(tmpvals.values_supplied & ~POINTSIZE_MASK) |
503		(vals->values_supplied & POINTSIZE_MASK);
504	    tmpvals.point_matrix[0] = vals->point_matrix[0];
505	    tmpvals.point_matrix[1] = vals->point_matrix[1];
506	    tmpvals.point_matrix[2] = vals->point_matrix[2];
507	    tmpvals.point_matrix[3] = vals->point_matrix[3];
508	}
509	if (vals->x >= 0)
510	    tmpvals.x = vals->x;
511	if (vals->y >= 0)
512	    tmpvals.y = vals->y;
513	if (vals->width >= 0)
514	    tmpvals.width = vals->width;
515	else if (vals->width < -1)	/* overload: -1 means wildcard */
516	    tmpvals.width = -vals->width;
517
518
519	p = ptr1 + 1;				/* weight field */
520	l = strchr(p, '-') - p;
521	sprintf(tmpBuf, "%*.*s", l, l, p);
522
523	p += l + 1;				/* slant field */
524	l = strchr(p, '-') - p;
525	sprintf(tmpBuf + strlen(tmpBuf), "-%*.*s", l, l, p);
526
527	p += l + 1;				/* setwidth_name */
528	l = strchr(p, '-') - p;
529	sprintf(tmpBuf + strlen(tmpBuf), "-%*.*s", l, l, p);
530
531	p += l + 1;				/* add_style_name field */
532	l = strchr(p, '-') - p;
533	sprintf(tmpBuf + strlen(tmpBuf), "-%*.*s", l, l, p);
534
535	strcat(tmpBuf, "-");
536	if ((tmpvals.values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY)
537	{
538	    char buffer[80];
539	    strcat(tmpBuf, "[");
540	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[0],
541		   buffer, 0));
542	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[1],
543		   buffer, 1));
544	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[2],
545		   buffer, 1));
546	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[3],
547		   buffer, 1));
548	    strcat(tmpBuf, "]");
549	}
550	else
551	{
552	    sprintf(tmpBuf + strlen(tmpBuf), "%d",
553		    (int)(tmpvals.pixel_matrix[3] + .5));
554	}
555	strcat(tmpBuf, "-");
556	if ((tmpvals.values_supplied & POINTSIZE_MASK) == POINTSIZE_ARRAY)
557	{
558	    char buffer[80];
559	    strcat(tmpBuf, "[");
560	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[0],
561		   buffer, 0));
562	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[1],
563		   buffer, 1));
564	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[2],
565		   buffer, 1));
566	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[3],
567		   buffer, 1));
568	    strcat(tmpBuf, "]");
569	}
570	else
571	{
572	    sprintf(tmpBuf + strlen(tmpBuf), "%d",
573		    (int)(tmpvals.point_matrix[3] * 10.0 + .5));
574	}
575	sprintf(tmpBuf + strlen(tmpBuf), "-%d-%d%*.*s%d%s",
576		tmpvals.x, tmpvals.y,
577		spacingLen, spacingLen, ptr3, tmpvals.width, ptr5);
578	strcpy(ptr1 + 1, tmpBuf);
579	if ((vals->values_supplied & CHARSUBSET_SPECIFIED) && !vals->nranges)
580	    strcat(fname, "[]");
581	else
582	    append_ranges(fname, vals->nranges, vals->ranges);
583	break;
584    }
585    return TRUE;
586}
587
588fsRange *FontParseRanges(char *name, int *nranges)
589{
590    int n;
591    unsigned long l;
592    char *p1, *p2;
593    fsRange *result = (fsRange *)0;
594
595    name = strchr(name, '-');
596    for (n = 1; name && n < 14; n++)
597	name = strchr(name + 1, '-');
598
599    *nranges = 0;
600    if (!name || !(p1 = strchr(name, '['))) return (fsRange *)0;
601    p1++;
602
603    while (*p1 && *p1 != ']')
604    {
605	fsRange thisrange;
606
607	l = strtol(p1, &p2, 0);
608	if (p2 == p1 || l > 0xffff) break;
609	thisrange.max_char_low = thisrange.min_char_low = l & 0xff;
610	thisrange.max_char_high = thisrange.min_char_high = l >> 8;
611
612	p1 = p2;
613	if (*p1 == ']' || *p1 == ' ')
614	{
615	    while (*p1 == ' ') p1++;
616	    if (add_range(&thisrange, nranges, &result, TRUE) != Successful)
617		break;
618	}
619	else if (*p1 == '_')
620	{
621	    l = strtol(++p1, &p2, 0);
622	    if (p2 == p1 || l > 0xffff) break;
623	    thisrange.max_char_low = l & 0xff;
624	    thisrange.max_char_high = l >> 8;
625	    p1 = p2;
626	    if (*p1 == ']' || *p1 == ' ')
627	    {
628		while (*p1 == ' ') p1++;
629		if (add_range(&thisrange, nranges, &result, TRUE) != Successful)
630		    break;
631	    }
632	}
633	else break;
634    }
635
636    return result;
637}
638