22f6119aeffa6053fcebdc2691a361caa756fd63
[framework/uifw/xorg/lib/libxfont.git] / src / util / fontutil.c
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 }