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