fontutil.c revision 23a0898a
1/* $Xorg: fontutil.c,v 1.4 2001/02/09 02:04:04 xorgcvs Exp $ */
2
3/*
4
5Copyright 1991, 1998  The Open Group
6
7Permission to use, copy, modify, distribute, and sell this software and its
8documentation for any purpose is hereby granted without fee, provided that
9the above copyright notice appear in all copies and that both that
10copyright notice and this permission notice appear in supporting
11documentation.
12
13The above copyright notice and this permission notice shall be included
14in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
20OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of The Open Group shall
25not be used in advertising or otherwise to promote the sale, use or
26other dealings in this Software without prior written authorization
27from The Open Group.
28
29*/
30/* $XFree86: xc/lib/font/util/fontutil.c,v 3.6 2001/10/28 03:32:46 tsi Exp $ */
31
32/*
33 * Author:  Keith Packard, MIT X Consortium
34 */
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39#include    <X11/fonts/fontmisc.h>
40#include    <X11/fonts/fontstruct.h>
41#include    <X11/fonts/FSproto.h>
42#include    <X11/fonts/fontutil.h>
43
44/* Define global here...  doesn't hurt the servers, and avoids
45   unresolved references in font clients.  */
46
47static int defaultGlyphCachingMode = DEFAULT_GLYPH_CACHING_MODE;
48int glyphCachingMode = DEFAULT_GLYPH_CACHING_MODE;
49
50void
51GetGlyphs(FontPtr font,
52	  unsigned long count,
53	  unsigned char *chars,
54	  FontEncoding fontEncoding,
55	  unsigned long *glyphcount,	/* RETURN */
56	  CharInfoPtr *glyphs)		/* RETURN */
57{
58    (*font->get_glyphs) (font, count, chars, fontEncoding, glyphcount, glyphs);
59}
60
61#define MIN(a,b)    ((a)<(b)?(a):(b))
62#define MAX(a,b)    ((a)>(b)?(a):(b))
63
64void
65QueryGlyphExtents(FontPtr pFont,
66		  CharInfoPtr *charinfo,
67		  unsigned long count,
68		  ExtentInfoRec *info)
69{
70    register unsigned long i;
71    xCharInfo  *pCI;
72
73    info->drawDirection = pFont->info.drawDirection;
74
75    info->fontAscent = pFont->info.fontAscent;
76    info->fontDescent = pFont->info.fontDescent;
77
78    if (count != 0) {
79
80	pCI = &((*charinfo)->metrics); charinfo++;
81	/* ignore nonexisting characters when calculating text extents */
82	if ( !((pCI->characterWidth == 0)
83	       && (pCI->rightSideBearing == 0)
84	       && (pCI->leftSideBearing == 0)
85	       && (pCI->ascent == 0)
86	       && (pCI->descent == 0)) ) {
87	    info->overallAscent = pCI->ascent;
88	    info->overallDescent = pCI->descent;
89	    info->overallLeft = pCI->leftSideBearing;
90	    info->overallRight = pCI->rightSideBearing;
91	    info->overallWidth = pCI->characterWidth;
92	}
93
94	if (pFont->info.constantMetrics && pFont->info.noOverlap) {
95	    info->overallWidth *= count;
96	    info->overallRight += (info->overallWidth -
97				   pCI->characterWidth);
98	} else {
99	    for (i = 1; i < count; i++) {
100		pCI = &((*charinfo)->metrics); charinfo++;
101		/* ignore nonexisting characters when calculating extents */
102		if ( !((pCI->characterWidth == 0)
103		       && (pCI->rightSideBearing == 0)
104		       && (pCI->leftSideBearing == 0)
105		       && (pCI->ascent == 0)
106		       && (pCI->descent == 0)) ) {
107		    info->overallAscent = MAX(
108					      info->overallAscent,
109					      pCI->ascent);
110		    info->overallDescent = MAX(
111					       info->overallDescent,
112					       pCI->descent);
113		    info->overallLeft = MIN(
114					    info->overallLeft,
115					    info->overallWidth + pCI->leftSideBearing);
116		    info->overallRight = MAX(
117					     info->overallRight,
118					     info->overallWidth + pCI->rightSideBearing);
119		    /*
120		     * yes, this order is correct; overallWidth IS incremented
121		     * last
122		     */
123		    info->overallWidth += pCI->characterWidth;
124		}
125	    }
126	}
127    } else {
128	info->overallAscent = 0;
129	info->overallDescent = 0;
130	info->overallWidth = 0;
131	info->overallLeft = 0;
132	info->overallRight = 0;
133    }
134}
135
136Bool
137QueryTextExtents(FontPtr pFont,
138		 unsigned long count,
139		 unsigned char *chars,
140		 ExtentInfoRec *info)
141{
142    xCharInfo     **charinfo;
143    unsigned long   n;
144    FontEncoding    encoding;
145    int             cm;
146    int             i;
147    unsigned long   t;
148    xCharInfo      *defaultChar = 0;
149    unsigned char   defc[2];
150    int             firstReal;
151
152    charinfo = (xCharInfo **) xalloc(count * sizeof(xCharInfo *));
153    if (!charinfo)
154	return FALSE;
155    encoding = TwoD16Bit;
156    if (pFont->info.lastRow == 0)
157	encoding = Linear16Bit;
158    (*pFont->get_metrics) (pFont, count, chars, encoding, &n, charinfo);
159
160    /* Do default character substitution as get_metrics doesn't */
161
162#define IsNonExistentChar(ci) (!(ci) || \
163			       ((ci)->ascent == 0 && \
164			       (ci)->descent == 0 && \
165			       (ci)->leftSideBearing == 0 && \
166			       (ci)->rightSideBearing == 0 && \
167			       (ci)->characterWidth == 0))
168
169    firstReal = n;
170    defc[0] = pFont->info.defaultCh >> 8;
171    defc[1] = pFont->info.defaultCh;
172    (*pFont->get_metrics) (pFont, 1, defc, encoding, &t, &defaultChar);
173    if ((IsNonExistentChar (defaultChar)))
174	defaultChar = 0;
175    for (i = 0; i < n; i++)
176    {
177	if ((IsNonExistentChar (charinfo[i])))
178	{
179	    if (!defaultChar)
180		continue;
181	    charinfo[i] = defaultChar;
182	}
183	if (firstReal == n)
184	    firstReal = i;
185    }
186    cm = pFont->info.constantMetrics;
187    pFont->info.constantMetrics = FALSE;
188    QueryGlyphExtents(pFont, (CharInfoPtr*) charinfo + firstReal,
189		      n - firstReal, info);
190    pFont->info.constantMetrics = cm;
191    xfree(charinfo);
192    return TRUE;
193}
194
195Bool
196ParseGlyphCachingMode(char *str)
197{
198    if (!strcmp(str, "none")) defaultGlyphCachingMode = CACHING_OFF;
199    else if (!strcmp(str, "all")) defaultGlyphCachingMode = CACHE_ALL_GLYPHS;
200    else if (!strcmp(str, "16")) defaultGlyphCachingMode = CACHE_16_BIT_GLYPHS;
201    else return FALSE;
202    return TRUE;
203}
204
205void
206InitGlyphCaching(void)
207{
208    /* Set glyphCachingMode to the mode the server hopes to
209       support.  DDX drivers that do not support the requested level
210       of glyph caching can call SetGlyphCachingMode to lower the
211       level of support.
212     */
213
214    glyphCachingMode = defaultGlyphCachingMode;
215}
216
217/* ddxen can call SetGlyphCachingMode to inform us of what level of glyph
218 * caching they can support.
219 */
220void
221SetGlyphCachingMode(int newmode)
222{
223    if ( (glyphCachingMode > newmode) && (newmode >= 0) )
224	glyphCachingMode = newmode;
225}
226
227#define range_alloc_granularity 16
228#define mincharp(p) ((p)->min_char_low + ((p)->min_char_high << 8))
229#define maxcharp(p) ((p)->max_char_low + ((p)->max_char_high << 8))
230
231/* add_range(): Add range to a list of ranges, with coalescence */
232int
233add_range(fsRange *newrange,
234	  int *nranges,
235	  fsRange **range,
236	  Bool charset_subset)
237{
238    int first, last, middle;
239    unsigned long keymin, keymax;
240    unsigned long ptrmin = 0, ptrmax = 0;
241    fsRange *ptr = NULL, *ptr1, *ptr2, *endptr;
242
243    /* There are two different ways to treat ranges:
244
245       1) Charset subsetting (support of the HP XLFD enhancements), in
246	  which a range of 0x1234,0x3456 means all numbers between
247	  0x1234 and 0x3456, and in which min and max might be swapped.
248
249       2) Row/column ranges, in which a range of 0x1234,0x3456 means the
250	  ranges 0x1234-0x1256, 0x1334-0x1356, ...  , 0x3434-0x3456.
251	  This is for support of glyph caching.
252
253       The choice of treatment is selected with the "charset_subset"
254       flag */
255
256    /* If newrange covers multiple rows; break up the rows */
257    if (!charset_subset && newrange->min_char_high != newrange->max_char_high)
258    {
259	int i, err = 0;
260	fsRange temprange;
261	for (i = newrange->min_char_high;
262	     i <= newrange->max_char_high;
263	     i++)
264	{
265	    temprange.min_char_low = newrange->min_char_low;
266	    temprange.max_char_low = newrange->max_char_low;
267	    temprange.min_char_high = temprange.max_char_high = i;
268	    err = add_range(&temprange, nranges, range, charset_subset);
269	    if (err != Successful) break;
270	}
271	return err;
272    }
273
274    keymin = mincharp(newrange);
275    keymax = maxcharp(newrange);
276
277    if (charset_subset && keymin > keymax)
278    {
279	unsigned long temp = keymin;
280	keymin = keymax;
281	keymax = temp;
282    }
283
284    /* add_range() maintains a sorted list; this makes possible coalescence
285       and binary searches */
286
287    /* Binary search for a range with which the new range can merge */
288
289    first = middle = 0;
290    last = *nranges - 1;
291    while (last >= first)
292    {
293	middle = (first + last) / 2;
294	ptr = (*range) + middle;
295	ptrmin = mincharp(ptr);
296	ptrmax = maxcharp(ptr);
297
298	if (ptrmin > 0 && keymax < ptrmin - 1) last = middle - 1;
299	else if (keymin > ptrmax + 1) first = middle + 1;
300	else if (!charset_subset)
301	{
302	    /* We might have a range with which to merge... IF the
303	       result doesn't cross rows */
304	    if (newrange->min_char_high != ptr->min_char_high)
305		last = first - 1;	/* Force adding a new range */
306	    break;
307	}
308	else break;	/* We have at least one range with which we can merge */
309    }
310
311    if (last < first)
312    {
313	/* Search failed; we need to add a new range to the list. */
314
315	/* Grow the list if necessary */
316	if (*nranges == 0 || *range == (fsRange *)0)
317	{
318	    *range = (fsRange *)xalloc(range_alloc_granularity *
319				       SIZEOF(fsRange));
320	    *nranges = 0;
321	}
322	else if (!(*nranges % range_alloc_granularity))
323	{
324	    *range = (fsRange *)xrealloc((char *)*range,
325					  (*nranges + range_alloc_granularity) *
326					  SIZEOF(fsRange));
327	}
328
329	/* If alloc failed, just return a null list */
330	if (*range == (fsRange *)0)
331	{
332	    *nranges = 0;
333	    return AllocError;
334	}
335
336	/* Should new entry go *at* or *after* ptr? */
337	ptr = (*range) + middle;
338	if (middle < *nranges && keymin > ptrmin) ptr++;	/* after */
339
340	/* Open up a space for our new range */
341	memmove((char *)(ptr + 1),
342		(char *)ptr,
343		(char *)(*range + *nranges) - (char *)ptr);
344
345	/* Insert the new range */
346	ptr->min_char_low = keymin & 0xff;
347	ptr->min_char_high = keymin >> 8;
348	ptr->max_char_low = keymax & 0xff;
349	ptr->max_char_high = keymax >> 8;
350
351	/* Update range count */
352	(*nranges)++;
353
354	/* Done */
355	return Successful;
356    }
357
358    /* Join our new range to that pointed to by "ptr" */
359    if (keymin < ptrmin)
360    {
361	ptr->min_char_low = keymin & 0xff;
362	ptr->min_char_high = keymin >> 8;
363    }
364    if (keymax > ptrmax)
365    {
366	ptr->max_char_low = keymax & 0xff;
367	ptr->max_char_high = keymax >> 8;
368    }
369
370    ptrmin = mincharp(ptr);
371    ptrmax = maxcharp(ptr);
372
373    endptr = *range + *nranges;
374
375    for (ptr1 = ptr; ptr1 >= *range; ptr1--)
376    {
377	if (ptrmin <= maxcharp(ptr1) + 1)
378	{
379	    if (!charset_subset && ptr->min_char_high != ptr1->min_char_high)
380		break;
381	    if (ptrmin >= mincharp(ptr1))
382		ptrmin = mincharp(ptr1);
383	}
384	else break;
385    }
386    for (ptr2 = ptr; ptr2 < endptr; ptr2++)
387    {
388	if ((ptr2->min_char_low == 0 && ptr2->min_char_high == 0) ||
389	    ptrmax >= mincharp(ptr2) - 1)
390	{
391	    if (!charset_subset && ptr->min_char_high != ptr2->min_char_high)
392		break;
393	    if (ptrmax <= maxcharp(ptr2))
394		ptrmax = maxcharp(ptr2);
395	}
396	else break;
397    }
398
399    /* We need to coalesce ranges between ptr1 and ptr2 exclusive */
400    ptr1++;
401    ptr2--;
402    if (ptr1 != ptr2)
403    {
404	memmove(ptr1, ptr2, (char *)endptr - (char *)ptr2);
405	*nranges -= (ptr2 - ptr1);
406    }
407
408    /* Write the new range into the range list */
409    ptr1->min_char_low = ptrmin & 0xff;
410    ptr1->min_char_high = ptrmin >> 8;
411    ptr1->max_char_low = ptrmax & 0xff;
412    ptr1->max_char_high = ptrmax >> 8;
413
414    return Successful;
415}
416
417/* It is difficult to find a good place for this. */
418#ifdef NEED_STRCASECMP
419int
420f_strcasecmp(const char *s1, const char *s2)
421{
422  char c1, c2;
423
424  if (*s1 == 0)
425    if (*s2 == 0)
426      return 0;
427    else
428      return 1;
429
430  c1 = (isupper (*s1) ? tolower (*s1) : *s1);
431  c2 = (isupper (*s2) ? tolower (*s2) : *s2);
432  while (c1 == c2) {
433    if (c1 == '\0')
434      return 0;
435    s1++;
436    s2++;
437    c1 = (isupper (*s1) ? tolower (*s1) : *s1);
438    c2 = (isupper (*s2) ? tolower (*s2) : *s2);
439  }
440  return c1 - c2;
441}
442#endif
443
444