fontscale.c revision 23a0898a
1/* $Xorg: fontscale.c,v 1.5 2001/02/09 02:04:03 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 in 14all copies or substantial portions of the Software. 15 16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 23Except as contained in this notice, the name of The Open Group shall not be 24used in advertising or otherwise to promote the sale, use or other dealings 25in this Software without prior written authorization from The Open Group. 26 27*/ 28/* $XFree86: xc/lib/font/fontfile/fontscale.c,v 3.9 2001/08/27 19:49:54 dawes Exp $ */ 29 30/* 31 * Author: Keith Packard, MIT X Consortium 32 */ 33 34#ifdef HAVE_CONFIG_H 35#include <config.h> 36#endif 37#include <X11/fonts/fntfilst.h> 38#ifdef _XOPEN_SOURCE 39#include <math.h> 40#else 41#define _XOPEN_SOURCE /* to get prototype for hypot on some systems */ 42#include <math.h> 43#undef _XOPEN_SOURCE 44#endif 45 46Bool 47FontFileAddScaledInstance (FontEntryPtr entry, FontScalablePtr vals, 48 FontPtr pFont, char *bitmapName) 49{ 50 FontScalableEntryPtr scalable; 51 FontScalableExtraPtr extra; 52 FontScaledPtr new; 53 int newsize; 54 55 scalable = &entry->u.scalable; 56 extra = scalable->extra; 57 if (extra->numScaled == extra->sizeScaled) 58 { 59 newsize = extra->sizeScaled + 4; 60 new = (FontScaledPtr) xrealloc (extra->scaled, 61 newsize * sizeof (FontScaledRec)); 62 if (!new) 63 return FALSE; 64 extra->sizeScaled = newsize; 65 extra->scaled = new; 66 } 67 new = &extra->scaled[extra->numScaled++]; 68 new->vals = *vals; 69 new->pFont = pFont; 70 new->bitmap = (FontEntryPtr) bitmapName; 71 if (pFont) 72 pFont->fpePrivate = (pointer) entry; 73 return TRUE; 74} 75 76/* Must call this after the directory is sorted */ 77 78void 79FontFileSwitchStringsToBitmapPointers (FontDirectoryPtr dir) 80{ 81 int s; 82 int b; 83 int i; 84 FontEntryPtr scalable; 85 FontEntryPtr nonScalable; 86 FontScaledPtr scaled; 87 FontScalableExtraPtr extra; 88 89 scalable = dir->scalable.entries; 90 nonScalable = dir->nonScalable.entries; 91 for (s = 0; s < dir->scalable.used; s++) 92 { 93 extra = scalable[s].u.scalable.extra; 94 scaled = extra->scaled; 95 for (i = 0; i < extra->numScaled; i++) 96 for (b = 0; b < dir->nonScalable.used; b++) 97 if (nonScalable[b].name.name == (char *) scaled[i].bitmap) 98 scaled[i].bitmap = &nonScalable[b]; 99 } 100} 101 102void 103FontFileRemoveScaledInstance (FontEntryPtr entry, FontPtr pFont) 104{ 105 FontScalableEntryPtr scalable; 106 FontScalableExtraPtr extra; 107 int i; 108 109 scalable = &entry->u.scalable; 110 extra = scalable->extra; 111 for (i = 0; i < extra->numScaled; i++) 112 { 113 if (extra->scaled[i].pFont == pFont) 114 { 115 if (extra->scaled[i].vals.ranges) 116 xfree (extra->scaled[i].vals.ranges); 117 extra->numScaled--; 118 for (; i < extra->numScaled; i++) 119 extra->scaled[i] = extra->scaled[i+1]; 120 } 121 } 122} 123 124Bool 125FontFileCompleteXLFD (FontScalablePtr vals, FontScalablePtr def) 126{ 127 FontResolutionPtr res; 128 int num_res; 129 double sx, sy, temp_matrix[4]; 130 double pixel_setsize_adjustment = 1.0; 131 /* 132 * If two of the three vertical scale values are specified, compute the 133 * third. If all three are specified, make sure they are consistent 134 * (within a pixel) 135 * 136 * One purpose of this procedure is to complete XLFD names in a 137 * repeatable manner. That is, if the user partially specifies 138 * a name (say, pixelsize but not pointsize), the results generated 139 * here result in a fully specified name that will result in the 140 * same font. 141 */ 142 143 res = GetClientResolutions(&num_res); 144 145 if (!(vals->values_supplied & PIXELSIZE_MASK) || 146 !(vals->values_supplied & POINTSIZE_MASK)) 147 { 148 /* If resolution(s) unspecified and cannot be computed from 149 pixelsize and pointsize, get appropriate defaults. */ 150 151 if (num_res) 152 { 153 if (vals->x <= 0) 154 vals->x = res->x_resolution; 155 if (vals->y <= 0) 156 vals->y = res->y_resolution; 157 } 158 159 if (vals->x <= 0) 160 vals->x = def->x; 161 if (vals->y <= 0) 162 vals->y = def->y; 163 } 164 else 165 { 166 /* If needed, compute resolution values from the pixel and 167 pointsize information we were given. This problem is 168 overdetermined (four equations, two unknowns), but we don't 169 check for inconsistencies here. If they exist, they will 170 show up in later tests for the point and pixel sizes. */ 171 172 if (vals->y <= 0) 173 { 174 double x = hypot(vals->pixel_matrix[1], vals->pixel_matrix[3]); 175 double y = hypot(vals->point_matrix[1], vals->point_matrix[3]); 176 if (y < EPS) return FALSE; 177 vals->y = (int)(x * 72.27 / y + .5); 178 } 179 if (vals->x <= 0) 180 { 181 /* If the pixelsize was given as an array, or as a scalar that 182 has been normalized for the pixel shape, we have enough 183 information to compute a separate horizontal resolution */ 184 185 if ((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY || 186 (vals->values_supplied & PIXELSIZE_MASK) == 187 PIXELSIZE_SCALAR_NORMALIZED) 188 { 189 double x = hypot(vals->pixel_matrix[0], vals->pixel_matrix[2]); 190 double y = hypot(vals->point_matrix[0], vals->point_matrix[2]); 191 if (y < EPS) return FALSE; 192 vals->x = (int)(x * 72.27 / y + .5); 193 } 194 else 195 { 196 /* Not enough information in the pixelsize array. Just 197 assume the pixels are square. */ 198 vals->x = vals->y; 199 } 200 } 201 } 202 203 if (vals->x <= 0 || vals->y <= 0) return FALSE; 204 205 /* If neither pixelsize nor pointsize is defined, take the pointsize 206 from the defaults structure we've been passed. */ 207 if (!(vals->values_supplied & PIXELSIZE_MASK) && 208 !(vals->values_supplied & POINTSIZE_MASK)) 209 { 210 if (num_res) 211 { 212 vals->point_matrix[0] = 213 vals->point_matrix[3] = (double)res->point_size / 10.0; 214 vals->point_matrix[1] = 215 vals->point_matrix[2] = 0; 216 vals->values_supplied = (vals->values_supplied & ~POINTSIZE_MASK) | 217 POINTSIZE_SCALAR; 218 } 219 else if (def->values_supplied & POINTSIZE_MASK) 220 { 221 vals->point_matrix[0] = def->point_matrix[0]; 222 vals->point_matrix[1] = def->point_matrix[1]; 223 vals->point_matrix[2] = def->point_matrix[2]; 224 vals->point_matrix[3] = def->point_matrix[3]; 225 vals->values_supplied = (vals->values_supplied & ~POINTSIZE_MASK) | 226 (def->values_supplied & POINTSIZE_MASK); 227 } 228 else return FALSE; 229 } 230 231 /* At this point, at least two of the three vertical scale values 232 should be specified. Our job now is to compute the missing ones 233 and check for agreement between overspecified values */ 234 235 /* If pixelsize was specified by a scalar, we need to fix the matrix 236 now that we know the resolutions. */ 237 if ((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_SCALAR) 238 { 239 /* pixel_setsize_adjustment used below to modify permissible 240 error in pixel/pointsize matching, since multiplying a 241 number rounded to integer changes the amount of the error 242 caused by the rounding */ 243 244 pixel_setsize_adjustment = (double)vals->x / (double)vals->y; 245 vals->pixel_matrix[0] *= pixel_setsize_adjustment; 246 vals->values_supplied = (vals->values_supplied & ~PIXELSIZE_MASK) | 247 PIXELSIZE_SCALAR_NORMALIZED; 248 } 249 250 sx = (double)vals->x / 72.27; 251 sy = (double)vals->y / 72.27; 252 253 /* If a pointsize was specified, make sure pixelsize is consistent 254 to within 1 pixel, then replace pixelsize with a consistent 255 floating-point value. */ 256 257 if (vals->values_supplied & POINTSIZE_MASK) 258 { 259 recompute_pixelsize: ; 260 temp_matrix[0] = vals->point_matrix[0] * sx; 261 temp_matrix[1] = vals->point_matrix[1] * sy; 262 temp_matrix[2] = vals->point_matrix[2] * sx; 263 temp_matrix[3] = vals->point_matrix[3] * sy; 264 if (vals->values_supplied & PIXELSIZE_MASK) 265 { 266 if (fabs(vals->pixel_matrix[0] - temp_matrix[0]) > 267 pixel_setsize_adjustment || 268 fabs(vals->pixel_matrix[1] - temp_matrix[1]) > 1 || 269 fabs(vals->pixel_matrix[2] - temp_matrix[2]) > 1 || 270 fabs(vals->pixel_matrix[3] - temp_matrix[3]) > 1) 271 return FALSE; 272 } 273 if ((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY && 274 (vals->values_supplied & POINTSIZE_MASK) == POINTSIZE_SCALAR) 275 { 276 /* In the special case that pixelsize came as an array and 277 pointsize as a scalar, recompute the pointsize matrix 278 from the pixelsize matrix. */ 279 goto recompute_pointsize; 280 } 281 282 /* Refresh pixel matrix with precise values computed from 283 pointsize and resolution. */ 284 vals->pixel_matrix[0] = temp_matrix[0]; 285 vals->pixel_matrix[1] = temp_matrix[1]; 286 vals->pixel_matrix[2] = temp_matrix[2]; 287 vals->pixel_matrix[3] = temp_matrix[3]; 288 289 /* Set values_supplied for pixel to match that for point */ 290 vals->values_supplied = 291 (vals->values_supplied & ~PIXELSIZE_MASK) | 292 (((vals->values_supplied & POINTSIZE_MASK) == POINTSIZE_ARRAY) ? 293 PIXELSIZE_ARRAY : PIXELSIZE_SCALAR_NORMALIZED); 294 } 295 else 296 { 297 /* Pointsize unspecified... compute from pixel size and 298 resolutions */ 299 recompute_pointsize: ; 300 if (fabs(sx) < EPS || fabs(sy) < EPS) return FALSE; 301 vals->point_matrix[0] = vals->pixel_matrix[0] / sx; 302 vals->point_matrix[1] = vals->pixel_matrix[1] / sy; 303 vals->point_matrix[2] = vals->pixel_matrix[2] / sx; 304 vals->point_matrix[3] = vals->pixel_matrix[3] / sy; 305 306 /* Set values_supplied for pixel to match that for point */ 307 vals->values_supplied = 308 (vals->values_supplied & ~POINTSIZE_MASK) | 309 (((vals->values_supplied & PIXELSIZE_MASK) == PIXELSIZE_ARRAY) ? 310 POINTSIZE_ARRAY : POINTSIZE_SCALAR); 311 312 /* If we computed scalar pointsize from scalar pixelsize, round 313 pointsize to decipoints and recompute pixelsize so we end up 314 with a repeatable name */ 315 if ((vals->values_supplied & POINTSIZE_MASK) == POINTSIZE_SCALAR) 316 { 317 /* Off-diagonal elements should be zero since no matrix was 318 specified. */ 319 vals->point_matrix[0] = 320 (double)(int)(vals->point_matrix[0] * 10.0 + .5) / 10.0; 321 vals->point_matrix[3] = 322 (double)(int)(vals->point_matrix[3] * 10.0 + .5) / 10.0; 323 goto recompute_pixelsize; 324 } 325 } 326 327 /* We've succeeded. Round everything to a few decimal places 328 for repeatability. */ 329 330 vals->pixel_matrix[0] = xlfd_round_double(vals->pixel_matrix[0]); 331 vals->pixel_matrix[1] = xlfd_round_double(vals->pixel_matrix[1]); 332 vals->pixel_matrix[2] = xlfd_round_double(vals->pixel_matrix[2]); 333 vals->pixel_matrix[3] = xlfd_round_double(vals->pixel_matrix[3]); 334 vals->point_matrix[0] = xlfd_round_double(vals->point_matrix[0]); 335 vals->point_matrix[1] = xlfd_round_double(vals->point_matrix[1]); 336 vals->point_matrix[2] = xlfd_round_double(vals->point_matrix[2]); 337 vals->point_matrix[3] = xlfd_round_double(vals->point_matrix[3]); 338 339 /* Fill in the deprecated fields for the benefit of rasterizers 340 that do not handle the matrices. */ 341 vals->point = vals->point_matrix[3] * 10; 342 vals->pixel = vals->pixel_matrix[3]; 343 344 return TRUE; 345} 346 347static Bool 348MatchScalable (FontScalablePtr a, FontScalablePtr b) 349{ 350 int i; 351 352 /* Some asymmetry here: we assume that the first argument (a) is 353 the table entry and the second (b) the item we're trying to match 354 (the key). We'll consider the fonts matched if the relevant 355 metrics match *and* if a) the table entry doesn't have charset 356 subsetting or b) the table entry has identical charset subsetting 357 to that in the key. We could add logic to check if the table 358 entry has a superset of the charset required by the key, but 359 we'll resist the urge for now. */ 360 361#define EQUAL(a,b) ((a)[0] == (b)[0] && \ 362 (a)[1] == (b)[1] && \ 363 (a)[2] == (b)[2] && \ 364 (a)[3] == (b)[3]) 365 366 if (!(a->x == b->x && 367 a->y == b->y && 368 (a->width == b->width || a->width == 0 || b->width == 0 || b->width == -1) && 369 (!(b->values_supplied & PIXELSIZE_MASK) || 370 ((a->values_supplied & PIXELSIZE_MASK) == 371 (b->values_supplied & PIXELSIZE_MASK) && 372 EQUAL(a->pixel_matrix, b->pixel_matrix))) && 373 (!(b->values_supplied & POINTSIZE_MASK) || 374 ((a->values_supplied & POINTSIZE_MASK) == 375 (b->values_supplied & POINTSIZE_MASK) && 376 EQUAL(a->point_matrix, b->point_matrix))) && 377 (a->nranges == 0 || a->nranges == b->nranges))) 378 return FALSE; 379 380 for (i = 0; i < a->nranges; i++) 381 if (a->ranges[i].min_char_low != b->ranges[i].min_char_low || 382 a->ranges[i].min_char_high != b->ranges[i].min_char_high || 383 a->ranges[i].max_char_low != b->ranges[i].max_char_low || 384 a->ranges[i].max_char_high != b->ranges[i].max_char_high) 385 return FALSE; 386 387 return TRUE; 388} 389 390FontScaledPtr 391FontFileFindScaledInstance (FontEntryPtr entry, FontScalablePtr vals, 392 int noSpecificSize) 393{ 394 FontScalableEntryPtr scalable; 395 FontScalableExtraPtr extra; 396 FontScalablePtr mvals; 397 int dist, i; 398 int mini; 399 double mindist; 400 register double temp, sum=0.0; 401 402#define NORMDIFF(a, b) ( \ 403 temp = (a)[0] - (b)[0], \ 404 sum = temp * temp, \ 405 temp = (a)[1] - (b)[1], \ 406 sum += temp * temp, \ 407 temp = (a)[2] - (b)[2], \ 408 sum += temp * temp, \ 409 temp = (a)[3] - (b)[3], \ 410 sum + temp * temp ) 411 412 scalable = &entry->u.scalable; 413 extra = scalable->extra; 414 if (noSpecificSize && extra->numScaled) 415 { 416 mini = 0; 417 mindist = NORMDIFF(extra->scaled[0].vals.point_matrix, 418 vals->point_matrix); 419 for (i = 1; i < extra->numScaled; i++) 420 { 421 if (extra->scaled[i].pFont && 422 !extra->scaled[i].pFont->info.cachable) continue; 423 mvals = &extra->scaled[i].vals; 424 dist = NORMDIFF(mvals->point_matrix, vals->point_matrix); 425 if (dist < mindist) 426 { 427 mindist = dist; 428 mini = i; 429 } 430 } 431 if (extra->scaled[mini].pFont && 432 !extra->scaled[mini].pFont->info.cachable) return 0; 433 return &extra->scaled[mini]; 434 } 435 else 436 { 437 /* See if we've scaled to this value yet */ 438 for (i = 0; i < extra->numScaled; i++) 439 { 440 if (extra->scaled[i].pFont && 441 !extra->scaled[i].pFont->info.cachable) continue; 442 if (MatchScalable (&extra->scaled[i].vals, vals)) 443 return &extra->scaled[i]; 444 } 445 } 446 return 0; 447} 448