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