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