fontutil.c revision 23a0898a
1/* $Xorg: fontutil.c,v 1.4 2001/02/09 02:04:04 xorgcvs Exp $ */ 2 3/* 4 5Copyright 1991, 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/fontutil.c,v 3.6 2001/10/28 03:32:46 tsi 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/FSproto.h> 42#include <X11/fonts/fontutil.h> 43 44/* Define global here... doesn't hurt the servers, and avoids 45 unresolved references in font clients. */ 46 47static int defaultGlyphCachingMode = DEFAULT_GLYPH_CACHING_MODE; 48int glyphCachingMode = DEFAULT_GLYPH_CACHING_MODE; 49 50void 51GetGlyphs(FontPtr font, 52 unsigned long count, 53 unsigned char *chars, 54 FontEncoding fontEncoding, 55 unsigned long *glyphcount, /* RETURN */ 56 CharInfoPtr *glyphs) /* RETURN */ 57{ 58 (*font->get_glyphs) (font, count, chars, fontEncoding, glyphcount, glyphs); 59} 60 61#define MIN(a,b) ((a)<(b)?(a):(b)) 62#define MAX(a,b) ((a)>(b)?(a):(b)) 63 64void 65QueryGlyphExtents(FontPtr pFont, 66 CharInfoPtr *charinfo, 67 unsigned long count, 68 ExtentInfoRec *info) 69{ 70 register unsigned long i; 71 xCharInfo *pCI; 72 73 info->drawDirection = pFont->info.drawDirection; 74 75 info->fontAscent = pFont->info.fontAscent; 76 info->fontDescent = pFont->info.fontDescent; 77 78 if (count != 0) { 79 80 pCI = &((*charinfo)->metrics); charinfo++; 81 /* ignore nonexisting characters when calculating text extents */ 82 if ( !((pCI->characterWidth == 0) 83 && (pCI->rightSideBearing == 0) 84 && (pCI->leftSideBearing == 0) 85 && (pCI->ascent == 0) 86 && (pCI->descent == 0)) ) { 87 info->overallAscent = pCI->ascent; 88 info->overallDescent = pCI->descent; 89 info->overallLeft = pCI->leftSideBearing; 90 info->overallRight = pCI->rightSideBearing; 91 info->overallWidth = pCI->characterWidth; 92 } 93 94 if (pFont->info.constantMetrics && pFont->info.noOverlap) { 95 info->overallWidth *= count; 96 info->overallRight += (info->overallWidth - 97 pCI->characterWidth); 98 } else { 99 for (i = 1; i < count; i++) { 100 pCI = &((*charinfo)->metrics); charinfo++; 101 /* ignore nonexisting characters when calculating extents */ 102 if ( !((pCI->characterWidth == 0) 103 && (pCI->rightSideBearing == 0) 104 && (pCI->leftSideBearing == 0) 105 && (pCI->ascent == 0) 106 && (pCI->descent == 0)) ) { 107 info->overallAscent = MAX( 108 info->overallAscent, 109 pCI->ascent); 110 info->overallDescent = MAX( 111 info->overallDescent, 112 pCI->descent); 113 info->overallLeft = MIN( 114 info->overallLeft, 115 info->overallWidth + pCI->leftSideBearing); 116 info->overallRight = MAX( 117 info->overallRight, 118 info->overallWidth + pCI->rightSideBearing); 119 /* 120 * yes, this order is correct; overallWidth IS incremented 121 * last 122 */ 123 info->overallWidth += pCI->characterWidth; 124 } 125 } 126 } 127 } else { 128 info->overallAscent = 0; 129 info->overallDescent = 0; 130 info->overallWidth = 0; 131 info->overallLeft = 0; 132 info->overallRight = 0; 133 } 134} 135 136Bool 137QueryTextExtents(FontPtr pFont, 138 unsigned long count, 139 unsigned char *chars, 140 ExtentInfoRec *info) 141{ 142 xCharInfo **charinfo; 143 unsigned long n; 144 FontEncoding encoding; 145 int cm; 146 int i; 147 unsigned long t; 148 xCharInfo *defaultChar = 0; 149 unsigned char defc[2]; 150 int firstReal; 151 152 charinfo = (xCharInfo **) xalloc(count * sizeof(xCharInfo *)); 153 if (!charinfo) 154 return FALSE; 155 encoding = TwoD16Bit; 156 if (pFont->info.lastRow == 0) 157 encoding = Linear16Bit; 158 (*pFont->get_metrics) (pFont, count, chars, encoding, &n, charinfo); 159 160 /* Do default character substitution as get_metrics doesn't */ 161 162#define IsNonExistentChar(ci) (!(ci) || \ 163 ((ci)->ascent == 0 && \ 164 (ci)->descent == 0 && \ 165 (ci)->leftSideBearing == 0 && \ 166 (ci)->rightSideBearing == 0 && \ 167 (ci)->characterWidth == 0)) 168 169 firstReal = n; 170 defc[0] = pFont->info.defaultCh >> 8; 171 defc[1] = pFont->info.defaultCh; 172 (*pFont->get_metrics) (pFont, 1, defc, encoding, &t, &defaultChar); 173 if ((IsNonExistentChar (defaultChar))) 174 defaultChar = 0; 175 for (i = 0; i < n; i++) 176 { 177 if ((IsNonExistentChar (charinfo[i]))) 178 { 179 if (!defaultChar) 180 continue; 181 charinfo[i] = defaultChar; 182 } 183 if (firstReal == n) 184 firstReal = i; 185 } 186 cm = pFont->info.constantMetrics; 187 pFont->info.constantMetrics = FALSE; 188 QueryGlyphExtents(pFont, (CharInfoPtr*) charinfo + firstReal, 189 n - firstReal, info); 190 pFont->info.constantMetrics = cm; 191 xfree(charinfo); 192 return TRUE; 193} 194 195Bool 196ParseGlyphCachingMode(char *str) 197{ 198 if (!strcmp(str, "none")) defaultGlyphCachingMode = CACHING_OFF; 199 else if (!strcmp(str, "all")) defaultGlyphCachingMode = CACHE_ALL_GLYPHS; 200 else if (!strcmp(str, "16")) defaultGlyphCachingMode = CACHE_16_BIT_GLYPHS; 201 else return FALSE; 202 return TRUE; 203} 204 205void 206InitGlyphCaching(void) 207{ 208 /* Set glyphCachingMode to the mode the server hopes to 209 support. DDX drivers that do not support the requested level 210 of glyph caching can call SetGlyphCachingMode to lower the 211 level of support. 212 */ 213 214 glyphCachingMode = defaultGlyphCachingMode; 215} 216 217/* ddxen can call SetGlyphCachingMode to inform us of what level of glyph 218 * caching they can support. 219 */ 220void 221SetGlyphCachingMode(int newmode) 222{ 223 if ( (glyphCachingMode > newmode) && (newmode >= 0) ) 224 glyphCachingMode = newmode; 225} 226 227#define range_alloc_granularity 16 228#define mincharp(p) ((p)->min_char_low + ((p)->min_char_high << 8)) 229#define maxcharp(p) ((p)->max_char_low + ((p)->max_char_high << 8)) 230 231/* add_range(): Add range to a list of ranges, with coalescence */ 232int 233add_range(fsRange *newrange, 234 int *nranges, 235 fsRange **range, 236 Bool charset_subset) 237{ 238 int first, last, middle; 239 unsigned long keymin, keymax; 240 unsigned long ptrmin = 0, ptrmax = 0; 241 fsRange *ptr = NULL, *ptr1, *ptr2, *endptr; 242 243 /* There are two different ways to treat ranges: 244 245 1) Charset subsetting (support of the HP XLFD enhancements), in 246 which a range of 0x1234,0x3456 means all numbers between 247 0x1234 and 0x3456, and in which min and max might be swapped. 248 249 2) Row/column ranges, in which a range of 0x1234,0x3456 means the 250 ranges 0x1234-0x1256, 0x1334-0x1356, ... , 0x3434-0x3456. 251 This is for support of glyph caching. 252 253 The choice of treatment is selected with the "charset_subset" 254 flag */ 255 256 /* If newrange covers multiple rows; break up the rows */ 257 if (!charset_subset && newrange->min_char_high != newrange->max_char_high) 258 { 259 int i, err = 0; 260 fsRange temprange; 261 for (i = newrange->min_char_high; 262 i <= newrange->max_char_high; 263 i++) 264 { 265 temprange.min_char_low = newrange->min_char_low; 266 temprange.max_char_low = newrange->max_char_low; 267 temprange.min_char_high = temprange.max_char_high = i; 268 err = add_range(&temprange, nranges, range, charset_subset); 269 if (err != Successful) break; 270 } 271 return err; 272 } 273 274 keymin = mincharp(newrange); 275 keymax = maxcharp(newrange); 276 277 if (charset_subset && keymin > keymax) 278 { 279 unsigned long temp = keymin; 280 keymin = keymax; 281 keymax = temp; 282 } 283 284 /* add_range() maintains a sorted list; this makes possible coalescence 285 and binary searches */ 286 287 /* Binary search for a range with which the new range can merge */ 288 289 first = middle = 0; 290 last = *nranges - 1; 291 while (last >= first) 292 { 293 middle = (first + last) / 2; 294 ptr = (*range) + middle; 295 ptrmin = mincharp(ptr); 296 ptrmax = maxcharp(ptr); 297 298 if (ptrmin > 0 && keymax < ptrmin - 1) last = middle - 1; 299 else if (keymin > ptrmax + 1) first = middle + 1; 300 else if (!charset_subset) 301 { 302 /* We might have a range with which to merge... IF the 303 result doesn't cross rows */ 304 if (newrange->min_char_high != ptr->min_char_high) 305 last = first - 1; /* Force adding a new range */ 306 break; 307 } 308 else break; /* We have at least one range with which we can merge */ 309 } 310 311 if (last < first) 312 { 313 /* Search failed; we need to add a new range to the list. */ 314 315 /* Grow the list if necessary */ 316 if (*nranges == 0 || *range == (fsRange *)0) 317 { 318 *range = (fsRange *)xalloc(range_alloc_granularity * 319 SIZEOF(fsRange)); 320 *nranges = 0; 321 } 322 else if (!(*nranges % range_alloc_granularity)) 323 { 324 *range = (fsRange *)xrealloc((char *)*range, 325 (*nranges + range_alloc_granularity) * 326 SIZEOF(fsRange)); 327 } 328 329 /* If alloc failed, just return a null list */ 330 if (*range == (fsRange *)0) 331 { 332 *nranges = 0; 333 return AllocError; 334 } 335 336 /* Should new entry go *at* or *after* ptr? */ 337 ptr = (*range) + middle; 338 if (middle < *nranges && keymin > ptrmin) ptr++; /* after */ 339 340 /* Open up a space for our new range */ 341 memmove((char *)(ptr + 1), 342 (char *)ptr, 343 (char *)(*range + *nranges) - (char *)ptr); 344 345 /* Insert the new range */ 346 ptr->min_char_low = keymin & 0xff; 347 ptr->min_char_high = keymin >> 8; 348 ptr->max_char_low = keymax & 0xff; 349 ptr->max_char_high = keymax >> 8; 350 351 /* Update range count */ 352 (*nranges)++; 353 354 /* Done */ 355 return Successful; 356 } 357 358 /* Join our new range to that pointed to by "ptr" */ 359 if (keymin < ptrmin) 360 { 361 ptr->min_char_low = keymin & 0xff; 362 ptr->min_char_high = keymin >> 8; 363 } 364 if (keymax > ptrmax) 365 { 366 ptr->max_char_low = keymax & 0xff; 367 ptr->max_char_high = keymax >> 8; 368 } 369 370 ptrmin = mincharp(ptr); 371 ptrmax = maxcharp(ptr); 372 373 endptr = *range + *nranges; 374 375 for (ptr1 = ptr; ptr1 >= *range; ptr1--) 376 { 377 if (ptrmin <= maxcharp(ptr1) + 1) 378 { 379 if (!charset_subset && ptr->min_char_high != ptr1->min_char_high) 380 break; 381 if (ptrmin >= mincharp(ptr1)) 382 ptrmin = mincharp(ptr1); 383 } 384 else break; 385 } 386 for (ptr2 = ptr; ptr2 < endptr; ptr2++) 387 { 388 if ((ptr2->min_char_low == 0 && ptr2->min_char_high == 0) || 389 ptrmax >= mincharp(ptr2) - 1) 390 { 391 if (!charset_subset && ptr->min_char_high != ptr2->min_char_high) 392 break; 393 if (ptrmax <= maxcharp(ptr2)) 394 ptrmax = maxcharp(ptr2); 395 } 396 else break; 397 } 398 399 /* We need to coalesce ranges between ptr1 and ptr2 exclusive */ 400 ptr1++; 401 ptr2--; 402 if (ptr1 != ptr2) 403 { 404 memmove(ptr1, ptr2, (char *)endptr - (char *)ptr2); 405 *nranges -= (ptr2 - ptr1); 406 } 407 408 /* Write the new range into the range list */ 409 ptr1->min_char_low = ptrmin & 0xff; 410 ptr1->min_char_high = ptrmin >> 8; 411 ptr1->max_char_low = ptrmax & 0xff; 412 ptr1->max_char_high = ptrmax >> 8; 413 414 return Successful; 415} 416 417/* It is difficult to find a good place for this. */ 418#ifdef NEED_STRCASECMP 419int 420f_strcasecmp(const char *s1, const char *s2) 421{ 422 char c1, c2; 423 424 if (*s1 == 0) 425 if (*s2 == 0) 426 return 0; 427 else 428 return 1; 429 430 c1 = (isupper (*s1) ? tolower (*s1) : *s1); 431 c2 = (isupper (*s2) ? tolower (*s2) : *s2); 432 while (c1 == c2) { 433 if (c1 == '\0') 434 return 0; 435 s1++; 436 s2++; 437 c1 = (isupper (*s1) ? tolower (*s1) : *s1); 438 c2 = (isupper (*s2) ? tolower (*s2) : *s2); 439 } 440 return c1 - c2; 441} 442#endif 443 444