fontxlfd.c revision 7f7f5e4e
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#include <float.h>
206
207/* if we have IEEE 754 fp, we can round to binary digits... */
208
209#if (FLT_RADIX == 2) && (DBL_DIG == 15) && (DBL_MANT_DIG == 53)
210
211#ifndef M_LN2
212#define M_LN2       0.69314718055994530942
213#endif
214#ifndef M_LN10
215#define M_LN10      2.30258509299404568402
216#endif
217
218/* convert # of decimal digits to # of binary digits */
219#define XLFD_NDIGITS_2 ((int)(XLFD_NDIGITS * M_LN10 / M_LN2 + 0.5))
220
221   union conv_d {
222      double d;
223      unsigned char b[8];
224   } d;
225   int i,j,k,d_exp;
226
227   if (x == 0)
228      return x;
229
230   /* do minor sanity check for IEEE 754 fp and correct byte order */
231   d.d = 1.0;
232   if (sizeof(double) == 8 && d.b[7] == 0x3f && d.b[6] == 0xf0) {
233
234      /*
235       * this code will round IEEE 754 double to XLFD_NDIGITS_2 binary digits
236       */
237
238      d.d = x;
239      d_exp = (d.b[7] << 4) | (d.b[6] >> 4);
240
241      i = (DBL_MANT_DIG-XLFD_NDIGITS_2) >> 3;
242      j = 1 << ((DBL_MANT_DIG-XLFD_NDIGITS_2) & 0x07);
243      for (; i<7; i++) {
244	 k = d.b[i] + j;
245	 d.b[i] = k;
246	 if (k & 0x100) j = 1;
247	 else break;
248      }
249      if ((i==7) && ((d.b[6] & 0xf0) != ((d_exp<<4) & 0xf0))) {
250	 /* mantissa overflow: increment exponent */
251	 d_exp = (d_exp & 0x800 ) | ((d_exp & 0x7ff) + 1);
252	 d.b[7] = d_exp >> 4;
253	 d.b[6] = (d.b[6] & 0x0f) | (d_exp << 4);
254      }
255
256      i = (DBL_MANT_DIG-XLFD_NDIGITS_2) >> 3;
257      j = 1 << ((DBL_MANT_DIG-XLFD_NDIGITS_2) & 0x07);
258      d.b[i] &= ~(j-1);
259      for (;--i>=0;) d.b[i] = 0;
260
261      return d.d;
262   }
263   else
264#endif
265#endif /* i386 || __i386__ */
266    {
267	/*
268	 * If not IEEE 754:  Let printf() do it for you.
269	 */
270
271	char formatbuf[40], buffer[40];
272
273	sprintf(formatbuf, "%%.%dlg", XLFD_NDIGITS);
274	sprintf(buffer, formatbuf, x);
275	return atof(buffer);
276    }
277}
278
279static char *
280GetMatrix(char *ptr, FontScalablePtr vals, int which)
281{
282    double *matrix;
283
284    if (which == PIXELSIZE_MASK)
285	matrix = vals->pixel_matrix;
286    else if (which == POINTSIZE_MASK)
287	matrix = vals->point_matrix;
288    else return (char *)0;
289
290    while (isspace(*ptr)) ptr++;
291    if (*ptr == '[')
292    {
293	/* This is a matrix containing real numbers.  It would be nice
294	   to use strtod() or sscanf() to read the numbers, but those
295	   don't handle '~' for minus and we cannot force them to use a
296	   "."  for the radix.  We'll have to do the hard work ourselves
297	   (in readreal()).  */
298
299	if ((ptr = readreal(++ptr, matrix + 0)) &&
300	    (ptr = readreal(ptr, matrix + 1)) &&
301	    (ptr = readreal(ptr, matrix + 2)) &&
302	    (ptr = readreal(ptr, matrix + 3)))
303	{
304	    while (isspace(*ptr)) ptr++;
305	    if (*ptr != ']')
306		ptr = (char *)0;
307	    else
308	    {
309		ptr++;
310		while (isspace(*ptr)) ptr++;
311		if (*ptr == '-')
312		{
313		    if (which == POINTSIZE_MASK)
314			vals->values_supplied |= POINTSIZE_ARRAY;
315		    else
316			vals->values_supplied |= PIXELSIZE_ARRAY;
317		}
318		else ptr = (char *)0;
319	    }
320	}
321    }
322    else
323    {
324	int value;
325	if ((ptr = GetInt(ptr, &value)))
326	{
327	    vals->values_supplied &= ~which;
328	    if (value > 0)
329	    {
330		matrix[3] = (double)value;
331		if (which == POINTSIZE_MASK)
332		{
333		    matrix[3] /= 10.0;
334		    vals->values_supplied |= POINTSIZE_SCALAR;
335		}
336		else
337		    vals->values_supplied |= PIXELSIZE_SCALAR;
338		/* If we're concocting the pixelsize array from a scalar,
339		   we will need to normalize element 0 for the pixel shape.
340		   This is done in FontFileCompleteXLFD(). */
341		matrix[0] = matrix[3];
342		matrix[1] = matrix[2] = 0.0;
343	    }
344	    else if (value < 0)
345	    {
346		if (which == POINTSIZE_MASK)
347		    vals->values_supplied |= POINTSIZE_WILDCARD;
348		else
349		    vals->values_supplied |= PIXELSIZE_WILDCARD;
350	    }
351	}
352    }
353    return ptr;
354}
355
356
357static void
358append_ranges(char *fname, int nranges, fsRange *ranges)
359{
360    if (nranges)
361    {
362        int i;
363
364        strcat(fname, "[");
365        for (i = 0; i < nranges && strlen(fname) < 1010; i++)
366        {
367	    if (i) strcat(fname, " ");
368	    sprintf(fname + strlen(fname), "%d",
369		    minchar(ranges[i]));
370	    if (ranges[i].min_char_low ==
371		ranges[i].max_char_low &&
372		ranges[i].min_char_high ==
373		ranges[i].max_char_high) continue;
374	    sprintf(fname + strlen(fname), "_%d",
375		    maxchar(ranges[i]));
376        }
377        strcat(fname, "]");
378    }
379}
380
381Bool
382FontParseXLFDName(char *fname, FontScalablePtr vals, int subst)
383{
384    register char *ptr;
385    register char *ptr1,
386               *ptr2,
387               *ptr3,
388               *ptr4;
389    register char *ptr5;
390    FontScalableRec tmpvals;
391    char        replaceChar = '0';
392    char        tmpBuf[1024];
393    int         spacingLen;
394    int		l;
395    char	*p;
396
397    bzero(&tmpvals, sizeof(tmpvals));
398    if (subst != FONT_XLFD_REPLACE_VALUE)
399	*vals = tmpvals;
400
401    if (!(*(ptr = fname) == '-' || (*ptr++ == '*' && *ptr == '-')) ||  /* fndry */
402	    !(ptr = strchr(ptr + 1, '-')) ||	/* family_name */
403	    !(ptr1 = ptr = strchr(ptr + 1, '-')) ||	/* weight_name */
404	    !(ptr = strchr(ptr + 1, '-')) ||	/* slant */
405	    !(ptr = strchr(ptr + 1, '-')) ||	/* setwidth_name */
406	    !(ptr = strchr(ptr + 1, '-')) ||	/* add_style_name */
407	    !(ptr = strchr(ptr + 1, '-')) ||	/* pixel_size */
408	    !(ptr = GetMatrix(ptr + 1, &tmpvals, PIXELSIZE_MASK)) ||
409	    !(ptr2 = ptr = GetMatrix(ptr + 1, &tmpvals, POINTSIZE_MASK)) ||
410	    !(ptr = GetInt(ptr + 1, &tmpvals.x)) ||	/* resolution_x */
411	    !(ptr3 = ptr = GetInt(ptr + 1, &tmpvals.y)) ||  /* resolution_y */
412	    !(ptr4 = ptr = strchr(ptr + 1, '-')) ||	/* spacing */
413	    !(ptr5 = ptr = GetInt(ptr + 1, &tmpvals.width)) || /* average_width */
414	    !(ptr = strchr(ptr + 1, '-')) ||	/* charset_registry */
415	    strchr(ptr + 1, '-'))/* charset_encoding */
416	return FALSE;
417
418    /* Lop off HP charset subsetting enhancement.  Interpreting this
419       field requires allocating some space in which to return the
420       results.  So, to prevent memory leaks, this procedure will simply
421       lop off and ignore charset subsetting, and initialize the
422       relevant vals fields to zero.  It's up to the caller to make its
423       own call to FontParseRanges() if it's interested in the charset
424       subsetting.  */
425
426    if (subst != FONT_XLFD_REPLACE_NONE &&
427	(p = strchr(strrchr(fname, '-'), '[')))
428    {
429	tmpvals.values_supplied |= CHARSUBSET_SPECIFIED;
430	*p = '\0';
431    }
432
433    /* Fill in deprecated fields for the benefit of rasterizers that care
434       about them. */
435    tmpvals.pixel = (tmpvals.pixel_matrix[3] >= 0) ?
436		    (int)(tmpvals.pixel_matrix[3] + .5) :
437		    (int)(tmpvals.pixel_matrix[3] - .5);
438    tmpvals.point = (tmpvals.point_matrix[3] >= 0) ?
439                    (int)(tmpvals.point_matrix[3] * 10 + .5) :
440                    (int)(tmpvals.point_matrix[3] * 10 - .5);
441
442    spacingLen = ptr4 - ptr3 + 1;
443
444    switch (subst) {
445    case FONT_XLFD_REPLACE_NONE:
446	*vals = tmpvals;
447	break;
448    case FONT_XLFD_REPLACE_STAR:
449	replaceChar = '*';
450    case FONT_XLFD_REPLACE_ZERO:
451	strcpy(tmpBuf, ptr2);
452	ptr5 = tmpBuf + (ptr5 - ptr2);
453	ptr3 = tmpBuf + (ptr3 - ptr2);
454	ptr2 = tmpBuf;
455	ptr = ptr1 + 1;
456
457	ptr = strchr(ptr, '-') + 1;		/* skip weight */
458	ptr = strchr(ptr, '-') + 1;		/* skip slant */
459	ptr = strchr(ptr, '-') + 1;		/* skip setwidth_name */
460	ptr = strchr(ptr, '-') + 1;		/* skip add_style_name */
461
462	if ((ptr - fname) + spacingLen + strlen(ptr5) + 10 >= (unsigned)1024)
463	    return FALSE;
464	*ptr++ = replaceChar;
465	*ptr++ = '-';
466	*ptr++ = replaceChar;
467	*ptr++ = '-';
468	*ptr++ = '*';
469	*ptr++ = '-';
470	*ptr++ = '*';
471	if (spacingLen > 2)
472	{
473	    memmove(ptr, ptr3, spacingLen);
474	    ptr += spacingLen;
475	}
476	else
477	{
478	    *ptr++ = '-';
479	    *ptr++ = '*';
480	    *ptr++ = '-';
481	}
482	*ptr++ = replaceChar;
483	strcpy(ptr, ptr5);
484	*vals = tmpvals;
485	break;
486    case FONT_XLFD_REPLACE_VALUE:
487	if (vals->values_supplied & PIXELSIZE_MASK)
488	{
489	    tmpvals.values_supplied =
490		(tmpvals.values_supplied & ~PIXELSIZE_MASK) |
491		(vals->values_supplied & PIXELSIZE_MASK);
492	    tmpvals.pixel_matrix[0] = vals->pixel_matrix[0];
493	    tmpvals.pixel_matrix[1] = vals->pixel_matrix[1];
494	    tmpvals.pixel_matrix[2] = vals->pixel_matrix[2];
495	    tmpvals.pixel_matrix[3] = vals->pixel_matrix[3];
496	}
497	if (vals->values_supplied & POINTSIZE_MASK)
498	{
499	    tmpvals.values_supplied =
500		(tmpvals.values_supplied & ~POINTSIZE_MASK) |
501		(vals->values_supplied & POINTSIZE_MASK);
502	    tmpvals.point_matrix[0] = vals->point_matrix[0];
503	    tmpvals.point_matrix[1] = vals->point_matrix[1];
504	    tmpvals.point_matrix[2] = vals->point_matrix[2];
505	    tmpvals.point_matrix[3] = vals->point_matrix[3];
506	}
507	if (vals->x >= 0)
508	    tmpvals.x = vals->x;
509	if (vals->y >= 0)
510	    tmpvals.y = vals->y;
511	if (vals->width >= 0)
512	    tmpvals.width = vals->width;
513	else if (vals->width < -1)	/* overload: -1 means wildcard */
514	    tmpvals.width = -vals->width;
515
516
517	p = ptr1 + 1;				/* weight field */
518	l = strchr(p, '-') - p;
519	sprintf(tmpBuf, "%*.*s", l, l, p);
520
521	p += l + 1;				/* slant field */
522	l = strchr(p, '-') - p;
523	sprintf(tmpBuf + strlen(tmpBuf), "-%*.*s", l, l, p);
524
525	p += l + 1;				/* setwidth_name */
526	l = strchr(p, '-') - p;
527	sprintf(tmpBuf + strlen(tmpBuf), "-%*.*s", l, l, p);
528
529	p += l + 1;				/* add_style_name field */
530	l = strchr(p, '-') - p;
531	sprintf(tmpBuf + strlen(tmpBuf), "-%*.*s", l, l, p);
532
533	strcat(tmpBuf, "-");
534	if ((tmpvals.values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY)
535	{
536	    char buffer[80];
537	    strcat(tmpBuf, "[");
538	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[0],
539		   buffer, 0));
540	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[1],
541		   buffer, 1));
542	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[2],
543		   buffer, 1));
544	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.pixel_matrix[3],
545		   buffer, 1));
546	    strcat(tmpBuf, "]");
547	}
548	else
549	{
550	    sprintf(tmpBuf + strlen(tmpBuf), "%d",
551		    (int)(tmpvals.pixel_matrix[3] + .5));
552	}
553	strcat(tmpBuf, "-");
554	if ((tmpvals.values_supplied & POINTSIZE_MASK) == POINTSIZE_ARRAY)
555	{
556	    char buffer[80];
557	    strcat(tmpBuf, "[");
558	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[0],
559		   buffer, 0));
560	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[1],
561		   buffer, 1));
562	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[2],
563		   buffer, 1));
564	    strcat(tmpBuf, xlfd_double_to_text(tmpvals.point_matrix[3],
565		   buffer, 1));
566	    strcat(tmpBuf, "]");
567	}
568	else
569	{
570	    sprintf(tmpBuf + strlen(tmpBuf), "%d",
571		    (int)(tmpvals.point_matrix[3] * 10.0 + .5));
572	}
573	sprintf(tmpBuf + strlen(tmpBuf), "-%d-%d%*.*s%d%s",
574		tmpvals.x, tmpvals.y,
575		spacingLen, spacingLen, ptr3, tmpvals.width, ptr5);
576	strcpy(ptr1 + 1, tmpBuf);
577	if ((vals->values_supplied & CHARSUBSET_SPECIFIED) && !vals->nranges)
578	    strcat(fname, "[]");
579	else
580	    append_ranges(fname, vals->nranges, vals->ranges);
581	break;
582    }
583    return TRUE;
584}
585
586fsRange *FontParseRanges(char *name, int *nranges)
587{
588    int n;
589    unsigned long l;
590    char *p1, *p2;
591    fsRange *result = (fsRange *)0;
592
593    name = strchr(name, '-');
594    for (n = 1; name && n < 14; n++)
595	name = strchr(name + 1, '-');
596
597    *nranges = 0;
598    if (!name || !(p1 = strchr(name, '['))) return (fsRange *)0;
599    p1++;
600
601    while (*p1 && *p1 != ']')
602    {
603	fsRange thisrange;
604
605	l = strtol(p1, &p2, 0);
606	if (p2 == p1 || l > 0xffff) break;
607	thisrange.max_char_low = thisrange.min_char_low = l & 0xff;
608	thisrange.max_char_high = thisrange.min_char_high = l >> 8;
609
610	p1 = p2;
611	if (*p1 == ']' || *p1 == ' ')
612	{
613	    while (*p1 == ' ') p1++;
614	    if (add_range(&thisrange, nranges, &result, TRUE) != Successful)
615		break;
616	}
617	else if (*p1 == '_')
618	{
619	    l = strtol(++p1, &p2, 0);
620	    if (p2 == p1 || l > 0xffff) break;
621	    thisrange.max_char_low = l & 0xff;
622	    thisrange.max_char_high = l >> 8;
623	    p1 = p2;
624	    if (*p1 == ']' || *p1 == ' ')
625	    {
626		while (*p1 == ' ') p1++;
627		if (add_range(&thisrange, nranges, &result, TRUE) != Successful)
628		    break;
629	    }
630	}
631	else break;
632    }
633
634    return result;
635}
636