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