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