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