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