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