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