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