fontutil.c revision 7f7f5e4e
123a0898aSmrg/* $Xorg: fontutil.c,v 1.4 2001/02/09 02:04:04 xorgcvs Exp $ */
223a0898aSmrg
323a0898aSmrg/*
423a0898aSmrg
523a0898aSmrgCopyright 1991, 1998  The Open Group
623a0898aSmrg
723a0898aSmrgPermission to use, copy, modify, distribute, and sell this software and its
823a0898aSmrgdocumentation for any purpose is hereby granted without fee, provided that
923a0898aSmrgthe above copyright notice appear in all copies and that both that
1023a0898aSmrgcopyright notice and this permission notice appear in supporting
1123a0898aSmrgdocumentation.
1223a0898aSmrg
1323a0898aSmrgThe above copyright notice and this permission notice shall be included
1423a0898aSmrgin all copies or substantial portions of the Software.
1523a0898aSmrg
1623a0898aSmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1723a0898aSmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1823a0898aSmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1923a0898aSmrgIN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
2023a0898aSmrgOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2123a0898aSmrgARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2223a0898aSmrgOTHER DEALINGS IN THE SOFTWARE.
2323a0898aSmrg
2423a0898aSmrgExcept as contained in this notice, the name of The Open Group shall
2523a0898aSmrgnot be used in advertising or otherwise to promote the sale, use or
2623a0898aSmrgother dealings in this Software without prior written authorization
2723a0898aSmrgfrom The Open Group.
2823a0898aSmrg
2923a0898aSmrg*/
3023a0898aSmrg/* $XFree86: xc/lib/font/util/fontutil.c,v 3.6 2001/10/28 03:32:46 tsi Exp $ */
3123a0898aSmrg
3223a0898aSmrg/*
3323a0898aSmrg * Author:  Keith Packard, MIT X Consortium
3423a0898aSmrg */
3523a0898aSmrg
3623a0898aSmrg#ifdef HAVE_CONFIG_H
3723a0898aSmrg#include <config.h>
3823a0898aSmrg#endif
3923a0898aSmrg#include    <X11/fonts/fontmisc.h>
4023a0898aSmrg#include    <X11/fonts/fontstruct.h>
4123a0898aSmrg#include    <X11/fonts/FSproto.h>
4223a0898aSmrg#include    <X11/fonts/fontutil.h>
4323a0898aSmrg
4423a0898aSmrg/* Define global here...  doesn't hurt the servers, and avoids
4523a0898aSmrg   unresolved references in font clients.  */
4623a0898aSmrg
4723a0898aSmrgstatic int defaultGlyphCachingMode = DEFAULT_GLYPH_CACHING_MODE;
4823a0898aSmrgint glyphCachingMode = DEFAULT_GLYPH_CACHING_MODE;
4923a0898aSmrg
5023a0898aSmrgvoid
5123a0898aSmrgGetGlyphs(FontPtr font,
5223a0898aSmrg	  unsigned long count,
5323a0898aSmrg	  unsigned char *chars,
5423a0898aSmrg	  FontEncoding fontEncoding,
5523a0898aSmrg	  unsigned long *glyphcount,	/* RETURN */
5623a0898aSmrg	  CharInfoPtr *glyphs)		/* RETURN */
5723a0898aSmrg{
5823a0898aSmrg    (*font->get_glyphs) (font, count, chars, fontEncoding, glyphcount, glyphs);
5923a0898aSmrg}
6023a0898aSmrg
6123a0898aSmrg#define MIN(a,b)    ((a)<(b)?(a):(b))
6223a0898aSmrg#define MAX(a,b)    ((a)>(b)?(a):(b))
6323a0898aSmrg
6423a0898aSmrgvoid
6523a0898aSmrgQueryGlyphExtents(FontPtr pFont,
6623a0898aSmrg		  CharInfoPtr *charinfo,
6723a0898aSmrg		  unsigned long count,
6823a0898aSmrg		  ExtentInfoRec *info)
6923a0898aSmrg{
7023a0898aSmrg    register unsigned long i;
7123a0898aSmrg    xCharInfo  *pCI;
7223a0898aSmrg
7323a0898aSmrg    info->drawDirection = pFont->info.drawDirection;
7423a0898aSmrg
7523a0898aSmrg    info->fontAscent = pFont->info.fontAscent;
7623a0898aSmrg    info->fontDescent = pFont->info.fontDescent;
7723a0898aSmrg
7823a0898aSmrg    if (count != 0) {
7923a0898aSmrg
8023a0898aSmrg	pCI = &((*charinfo)->metrics); charinfo++;
8123a0898aSmrg	/* ignore nonexisting characters when calculating text extents */
8223a0898aSmrg	if ( !((pCI->characterWidth == 0)
8323a0898aSmrg	       && (pCI->rightSideBearing == 0)
8423a0898aSmrg	       && (pCI->leftSideBearing == 0)
8523a0898aSmrg	       && (pCI->ascent == 0)
8623a0898aSmrg	       && (pCI->descent == 0)) ) {
8723a0898aSmrg	    info->overallAscent = pCI->ascent;
8823a0898aSmrg	    info->overallDescent = pCI->descent;
8923a0898aSmrg	    info->overallLeft = pCI->leftSideBearing;
9023a0898aSmrg	    info->overallRight = pCI->rightSideBearing;
9123a0898aSmrg	    info->overallWidth = pCI->characterWidth;
9223a0898aSmrg	}
9323a0898aSmrg
9423a0898aSmrg	if (pFont->info.constantMetrics && pFont->info.noOverlap) {
9523a0898aSmrg	    info->overallWidth *= count;
9623a0898aSmrg	    info->overallRight += (info->overallWidth -
9723a0898aSmrg				   pCI->characterWidth);
9823a0898aSmrg	} else {
9923a0898aSmrg	    for (i = 1; i < count; i++) {
10023a0898aSmrg		pCI = &((*charinfo)->metrics); charinfo++;
10123a0898aSmrg		/* ignore nonexisting characters when calculating extents */
10223a0898aSmrg		if ( !((pCI->characterWidth == 0)
10323a0898aSmrg		       && (pCI->rightSideBearing == 0)
10423a0898aSmrg		       && (pCI->leftSideBearing == 0)
10523a0898aSmrg		       && (pCI->ascent == 0)
10623a0898aSmrg		       && (pCI->descent == 0)) ) {
10723a0898aSmrg		    info->overallAscent = MAX(
10823a0898aSmrg					      info->overallAscent,
10923a0898aSmrg					      pCI->ascent);
11023a0898aSmrg		    info->overallDescent = MAX(
11123a0898aSmrg					       info->overallDescent,
11223a0898aSmrg					       pCI->descent);
11323a0898aSmrg		    info->overallLeft = MIN(
11423a0898aSmrg					    info->overallLeft,
11523a0898aSmrg					    info->overallWidth + pCI->leftSideBearing);
11623a0898aSmrg		    info->overallRight = MAX(
11723a0898aSmrg					     info->overallRight,
11823a0898aSmrg					     info->overallWidth + pCI->rightSideBearing);
11923a0898aSmrg		    /*
12023a0898aSmrg		     * yes, this order is correct; overallWidth IS incremented
12123a0898aSmrg		     * last
12223a0898aSmrg		     */
12323a0898aSmrg		    info->overallWidth += pCI->characterWidth;
12423a0898aSmrg		}
12523a0898aSmrg	    }
12623a0898aSmrg	}
12723a0898aSmrg    } else {
12823a0898aSmrg	info->overallAscent = 0;
12923a0898aSmrg	info->overallDescent = 0;
13023a0898aSmrg	info->overallWidth = 0;
13123a0898aSmrg	info->overallLeft = 0;
13223a0898aSmrg	info->overallRight = 0;
13323a0898aSmrg    }
13423a0898aSmrg}
13523a0898aSmrg
13623a0898aSmrgBool
13723a0898aSmrgQueryTextExtents(FontPtr pFont,
13823a0898aSmrg		 unsigned long count,
13923a0898aSmrg		 unsigned char *chars,
14023a0898aSmrg		 ExtentInfoRec *info)
14123a0898aSmrg{
14223a0898aSmrg    xCharInfo     **charinfo;
14323a0898aSmrg    unsigned long   n;
14423a0898aSmrg    FontEncoding    encoding;
14523a0898aSmrg    int             cm;
14623a0898aSmrg    int             i;
14723a0898aSmrg    unsigned long   t;
14823a0898aSmrg    xCharInfo      *defaultChar = 0;
14923a0898aSmrg    unsigned char   defc[2];
15023a0898aSmrg    int             firstReal;
15123a0898aSmrg
1527f7f5e4eSmrg    charinfo = malloc(count * sizeof(xCharInfo *));
15323a0898aSmrg    if (!charinfo)
15423a0898aSmrg	return FALSE;
15523a0898aSmrg    encoding = TwoD16Bit;
15623a0898aSmrg    if (pFont->info.lastRow == 0)
15723a0898aSmrg	encoding = Linear16Bit;
15823a0898aSmrg    (*pFont->get_metrics) (pFont, count, chars, encoding, &n, charinfo);
15923a0898aSmrg
16023a0898aSmrg    /* Do default character substitution as get_metrics doesn't */
16123a0898aSmrg
16223a0898aSmrg#define IsNonExistentChar(ci) (!(ci) || \
16323a0898aSmrg			       ((ci)->ascent == 0 && \
16423a0898aSmrg			       (ci)->descent == 0 && \
16523a0898aSmrg			       (ci)->leftSideBearing == 0 && \
16623a0898aSmrg			       (ci)->rightSideBearing == 0 && \
16723a0898aSmrg			       (ci)->characterWidth == 0))
16823a0898aSmrg
16923a0898aSmrg    firstReal = n;
17023a0898aSmrg    defc[0] = pFont->info.defaultCh >> 8;
17123a0898aSmrg    defc[1] = pFont->info.defaultCh;
17223a0898aSmrg    (*pFont->get_metrics) (pFont, 1, defc, encoding, &t, &defaultChar);
17323a0898aSmrg    if ((IsNonExistentChar (defaultChar)))
17423a0898aSmrg	defaultChar = 0;
17523a0898aSmrg    for (i = 0; i < n; i++)
17623a0898aSmrg    {
17723a0898aSmrg	if ((IsNonExistentChar (charinfo[i])))
17823a0898aSmrg	{
17923a0898aSmrg	    if (!defaultChar)
18023a0898aSmrg		continue;
18123a0898aSmrg	    charinfo[i] = defaultChar;
18223a0898aSmrg	}
18323a0898aSmrg	if (firstReal == n)
18423a0898aSmrg	    firstReal = i;
18523a0898aSmrg    }
18623a0898aSmrg    cm = pFont->info.constantMetrics;
18723a0898aSmrg    pFont->info.constantMetrics = FALSE;
18823a0898aSmrg    QueryGlyphExtents(pFont, (CharInfoPtr*) charinfo + firstReal,
18923a0898aSmrg		      n - firstReal, info);
19023a0898aSmrg    pFont->info.constantMetrics = cm;
1917f7f5e4eSmrg    free(charinfo);
19223a0898aSmrg    return TRUE;
19323a0898aSmrg}
19423a0898aSmrg
19523a0898aSmrgBool
19623a0898aSmrgParseGlyphCachingMode(char *str)
19723a0898aSmrg{
19823a0898aSmrg    if (!strcmp(str, "none")) defaultGlyphCachingMode = CACHING_OFF;
19923a0898aSmrg    else if (!strcmp(str, "all")) defaultGlyphCachingMode = CACHE_ALL_GLYPHS;
20023a0898aSmrg    else if (!strcmp(str, "16")) defaultGlyphCachingMode = CACHE_16_BIT_GLYPHS;
20123a0898aSmrg    else return FALSE;
20223a0898aSmrg    return TRUE;
20323a0898aSmrg}
20423a0898aSmrg
20523a0898aSmrgvoid
20623a0898aSmrgInitGlyphCaching(void)
20723a0898aSmrg{
20823a0898aSmrg    /* Set glyphCachingMode to the mode the server hopes to
20923a0898aSmrg       support.  DDX drivers that do not support the requested level
21023a0898aSmrg       of glyph caching can call SetGlyphCachingMode to lower the
21123a0898aSmrg       level of support.
21223a0898aSmrg     */
21323a0898aSmrg
21423a0898aSmrg    glyphCachingMode = defaultGlyphCachingMode;
21523a0898aSmrg}
21623a0898aSmrg
21723a0898aSmrg/* ddxen can call SetGlyphCachingMode to inform us of what level of glyph
21823a0898aSmrg * caching they can support.
21923a0898aSmrg */
22023a0898aSmrgvoid
22123a0898aSmrgSetGlyphCachingMode(int newmode)
22223a0898aSmrg{
22323a0898aSmrg    if ( (glyphCachingMode > newmode) && (newmode >= 0) )
22423a0898aSmrg	glyphCachingMode = newmode;
22523a0898aSmrg}
22623a0898aSmrg
22723a0898aSmrg#define range_alloc_granularity 16
22823a0898aSmrg#define mincharp(p) ((p)->min_char_low + ((p)->min_char_high << 8))
22923a0898aSmrg#define maxcharp(p) ((p)->max_char_low + ((p)->max_char_high << 8))
23023a0898aSmrg
23123a0898aSmrg/* add_range(): Add range to a list of ranges, with coalescence */
23223a0898aSmrgint
23323a0898aSmrgadd_range(fsRange *newrange,
23423a0898aSmrg	  int *nranges,
23523a0898aSmrg	  fsRange **range,
23623a0898aSmrg	  Bool charset_subset)
23723a0898aSmrg{
23823a0898aSmrg    int first, last, middle;
23923a0898aSmrg    unsigned long keymin, keymax;
24023a0898aSmrg    unsigned long ptrmin = 0, ptrmax = 0;
24123a0898aSmrg    fsRange *ptr = NULL, *ptr1, *ptr2, *endptr;
24223a0898aSmrg
24323a0898aSmrg    /* There are two different ways to treat ranges:
24423a0898aSmrg
24523a0898aSmrg       1) Charset subsetting (support of the HP XLFD enhancements), in
24623a0898aSmrg	  which a range of 0x1234,0x3456 means all numbers between
24723a0898aSmrg	  0x1234 and 0x3456, and in which min and max might be swapped.
24823a0898aSmrg
24923a0898aSmrg       2) Row/column ranges, in which a range of 0x1234,0x3456 means the
25023a0898aSmrg	  ranges 0x1234-0x1256, 0x1334-0x1356, ...  , 0x3434-0x3456.
25123a0898aSmrg	  This is for support of glyph caching.
25223a0898aSmrg
25323a0898aSmrg       The choice of treatment is selected with the "charset_subset"
25423a0898aSmrg       flag */
25523a0898aSmrg
25623a0898aSmrg    /* If newrange covers multiple rows; break up the rows */
25723a0898aSmrg    if (!charset_subset && newrange->min_char_high != newrange->max_char_high)
25823a0898aSmrg    {
25923a0898aSmrg	int i, err = 0;
26023a0898aSmrg	fsRange temprange;
26123a0898aSmrg	for (i = newrange->min_char_high;
26223a0898aSmrg	     i <= newrange->max_char_high;
26323a0898aSmrg	     i++)
26423a0898aSmrg	{
26523a0898aSmrg	    temprange.min_char_low = newrange->min_char_low;
26623a0898aSmrg	    temprange.max_char_low = newrange->max_char_low;
26723a0898aSmrg	    temprange.min_char_high = temprange.max_char_high = i;
26823a0898aSmrg	    err = add_range(&temprange, nranges, range, charset_subset);
26923a0898aSmrg	    if (err != Successful) break;
27023a0898aSmrg	}
27123a0898aSmrg	return err;
27223a0898aSmrg    }
27323a0898aSmrg
27423a0898aSmrg    keymin = mincharp(newrange);
27523a0898aSmrg    keymax = maxcharp(newrange);
27623a0898aSmrg
27723a0898aSmrg    if (charset_subset && keymin > keymax)
27823a0898aSmrg    {
27923a0898aSmrg	unsigned long temp = keymin;
28023a0898aSmrg	keymin = keymax;
28123a0898aSmrg	keymax = temp;
28223a0898aSmrg    }
28323a0898aSmrg
28423a0898aSmrg    /* add_range() maintains a sorted list; this makes possible coalescence
28523a0898aSmrg       and binary searches */
28623a0898aSmrg
28723a0898aSmrg    /* Binary search for a range with which the new range can merge */
28823a0898aSmrg
28923a0898aSmrg    first = middle = 0;
29023a0898aSmrg    last = *nranges - 1;
29123a0898aSmrg    while (last >= first)
29223a0898aSmrg    {
29323a0898aSmrg	middle = (first + last) / 2;
29423a0898aSmrg	ptr = (*range) + middle;
29523a0898aSmrg	ptrmin = mincharp(ptr);
29623a0898aSmrg	ptrmax = maxcharp(ptr);
29723a0898aSmrg
29823a0898aSmrg	if (ptrmin > 0 && keymax < ptrmin - 1) last = middle - 1;
29923a0898aSmrg	else if (keymin > ptrmax + 1) first = middle + 1;
30023a0898aSmrg	else if (!charset_subset)
30123a0898aSmrg	{
30223a0898aSmrg	    /* We might have a range with which to merge... IF the
30323a0898aSmrg	       result doesn't cross rows */
30423a0898aSmrg	    if (newrange->min_char_high != ptr->min_char_high)
30523a0898aSmrg		last = first - 1;	/* Force adding a new range */
30623a0898aSmrg	    break;
30723a0898aSmrg	}
30823a0898aSmrg	else break;	/* We have at least one range with which we can merge */
30923a0898aSmrg    }
31023a0898aSmrg
31123a0898aSmrg    if (last < first)
31223a0898aSmrg    {
31323a0898aSmrg	/* Search failed; we need to add a new range to the list. */
31423a0898aSmrg
31523a0898aSmrg	/* Grow the list if necessary */
31623a0898aSmrg	if (*nranges == 0 || *range == (fsRange *)0)
31723a0898aSmrg	{
3187f7f5e4eSmrg	    *range = malloc(range_alloc_granularity * SIZEOF(fsRange));
31923a0898aSmrg	    *nranges = 0;
32023a0898aSmrg	}
32123a0898aSmrg	else if (!(*nranges % range_alloc_granularity))
32223a0898aSmrg	{
3237f7f5e4eSmrg	    *range = realloc(*range, (*nranges + range_alloc_granularity) *
3247f7f5e4eSmrg				      SIZEOF(fsRange));
32523a0898aSmrg	}
32623a0898aSmrg
32723a0898aSmrg	/* If alloc failed, just return a null list */
32823a0898aSmrg	if (*range == (fsRange *)0)
32923a0898aSmrg	{
33023a0898aSmrg	    *nranges = 0;
33123a0898aSmrg	    return AllocError;
33223a0898aSmrg	}
33323a0898aSmrg
33423a0898aSmrg	/* Should new entry go *at* or *after* ptr? */
33523a0898aSmrg	ptr = (*range) + middle;
33623a0898aSmrg	if (middle < *nranges && keymin > ptrmin) ptr++;	/* after */
33723a0898aSmrg
33823a0898aSmrg	/* Open up a space for our new range */
33923a0898aSmrg	memmove((char *)(ptr + 1),
34023a0898aSmrg		(char *)ptr,
34123a0898aSmrg		(char *)(*range + *nranges) - (char *)ptr);
34223a0898aSmrg
34323a0898aSmrg	/* Insert the new range */
34423a0898aSmrg	ptr->min_char_low = keymin & 0xff;
34523a0898aSmrg	ptr->min_char_high = keymin >> 8;
34623a0898aSmrg	ptr->max_char_low = keymax & 0xff;
34723a0898aSmrg	ptr->max_char_high = keymax >> 8;
34823a0898aSmrg
34923a0898aSmrg	/* Update range count */
35023a0898aSmrg	(*nranges)++;
35123a0898aSmrg
35223a0898aSmrg	/* Done */
35323a0898aSmrg	return Successful;
35423a0898aSmrg    }
35523a0898aSmrg
35623a0898aSmrg    /* Join our new range to that pointed to by "ptr" */
35723a0898aSmrg    if (keymin < ptrmin)
35823a0898aSmrg    {
35923a0898aSmrg	ptr->min_char_low = keymin & 0xff;
36023a0898aSmrg	ptr->min_char_high = keymin >> 8;
36123a0898aSmrg    }
36223a0898aSmrg    if (keymax > ptrmax)
36323a0898aSmrg    {
36423a0898aSmrg	ptr->max_char_low = keymax & 0xff;
36523a0898aSmrg	ptr->max_char_high = keymax >> 8;
36623a0898aSmrg    }
36723a0898aSmrg
36823a0898aSmrg    ptrmin = mincharp(ptr);
36923a0898aSmrg    ptrmax = maxcharp(ptr);
37023a0898aSmrg
37123a0898aSmrg    endptr = *range + *nranges;
37223a0898aSmrg
37323a0898aSmrg    for (ptr1 = ptr; ptr1 >= *range; ptr1--)
37423a0898aSmrg    {
37523a0898aSmrg	if (ptrmin <= maxcharp(ptr1) + 1)
37623a0898aSmrg	{
37723a0898aSmrg	    if (!charset_subset && ptr->min_char_high != ptr1->min_char_high)
37823a0898aSmrg		break;
37923a0898aSmrg	    if (ptrmin >= mincharp(ptr1))
38023a0898aSmrg		ptrmin = mincharp(ptr1);
38123a0898aSmrg	}
38223a0898aSmrg	else break;
38323a0898aSmrg    }
38423a0898aSmrg    for (ptr2 = ptr; ptr2 < endptr; ptr2++)
38523a0898aSmrg    {
38623a0898aSmrg	if ((ptr2->min_char_low == 0 && ptr2->min_char_high == 0) ||
38723a0898aSmrg	    ptrmax >= mincharp(ptr2) - 1)
38823a0898aSmrg	{
38923a0898aSmrg	    if (!charset_subset && ptr->min_char_high != ptr2->min_char_high)
39023a0898aSmrg		break;
39123a0898aSmrg	    if (ptrmax <= maxcharp(ptr2))
39223a0898aSmrg		ptrmax = maxcharp(ptr2);
39323a0898aSmrg	}
39423a0898aSmrg	else break;
39523a0898aSmrg    }
39623a0898aSmrg
39723a0898aSmrg    /* We need to coalesce ranges between ptr1 and ptr2 exclusive */
39823a0898aSmrg    ptr1++;
39923a0898aSmrg    ptr2--;
40023a0898aSmrg    if (ptr1 != ptr2)
40123a0898aSmrg    {
40223a0898aSmrg	memmove(ptr1, ptr2, (char *)endptr - (char *)ptr2);
40323a0898aSmrg	*nranges -= (ptr2 - ptr1);
40423a0898aSmrg    }
40523a0898aSmrg
40623a0898aSmrg    /* Write the new range into the range list */
40723a0898aSmrg    ptr1->min_char_low = ptrmin & 0xff;
40823a0898aSmrg    ptr1->min_char_high = ptrmin >> 8;
40923a0898aSmrg    ptr1->max_char_low = ptrmax & 0xff;
41023a0898aSmrg    ptr1->max_char_high = ptrmax >> 8;
41123a0898aSmrg
41223a0898aSmrg    return Successful;
41323a0898aSmrg}
414