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