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 "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
46 static int defaultGlyphCachingMode = DEFAULT_GLYPH_CACHING_MODE;
47 int 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
52 void
xfont2_query_glyph_extents(FontPtr pFont,CharInfoPtr * charinfo,unsigned long count,ExtentInfoRec * info)53 xfont2_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
124 Bool
xfont2_query_text_extents(FontPtr pFont,unsigned long count,unsigned char * chars,ExtentInfoRec * info)125 xfont2_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
183 Bool
xfont2_parse_glyph_caching_mode(char * str)184 xfont2_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
193 void
xfont2_init_glyph_caching(void)194 xfont2_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 */
208 void
xfont2_set_glyph_caching_mode(int newmode)209 xfont2_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 */
220 int
add_range(fsRange * newrange,int * nranges,fsRange ** range,Bool charset_subset)221 add_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