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