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