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