1 /* QuesoGLC
2  * A free implementation of the OpenGL Character Renderer (GLC)
3  * Copyright (c) 2002, 2004-2008, Bertrand Coconnier
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2.1 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 /* $Id: ocharmap.c 800 2008-06-04 21:47:50Z bcoconni $ */
20 
21 /** \file
22  * defines the object __GLCcharMap which manage the charmaps of both the fonts
23  * and the masters. One of the purpose of this object is to encapsulate the
24  * FcCharSet structure from Fontconfig and to add it some more functionalities.
25  * It also allows to centralize the character map management for easier
26  * maintenance.
27  */
28 
29 #include "internal.h"
30 
31 
32 
33 /* Constructor of the object : it allocates memory and initializes the member
34  * of the new object.
35  * The user must give the FcPattern of the font or the master (which may be NULL
36  * in which case the character map will be empty).
37  */
__glcCharMapCreate(__GLCmaster * inMaster,__GLCcontext * inContext)38 __GLCcharMap* __glcCharMapCreate(__GLCmaster* inMaster, __GLCcontext* inContext)
39 {
40   __GLCcharMap* This = NULL;
41 
42   This = (__GLCcharMap*)__glcMalloc(sizeof(__GLCcharMap));
43   if (!This) {
44     __glcRaiseError(GLC_RESOURCE_ERROR);
45     return NULL;
46   }
47 
48   This->charSet = FcCharSetCreate();
49   if (!This->charSet) {
50     __glcRaiseError(GLC_RESOURCE_ERROR);
51     __glcFree(This);
52     return NULL;
53   }
54 
55   if (inMaster) {
56     FcCharSet* charSet = NULL;
57     FcFontSet* fontSet = NULL;
58     int i = 0;
59     FcObjectSet* objectSet = NULL;
60     FcPattern* pattern = FcPatternCreate();
61 
62     if (!pattern) {
63       __glcRaiseError(GLC_RESOURCE_ERROR);
64       FcCharSetDestroy(This->charSet);
65       __glcFree(This);
66       return NULL;
67     }
68 
69     objectSet = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, FC_SPACING, FC_OUTLINE,
70 				 FC_CHARSET, NULL);
71     if (!objectSet) {
72       __glcRaiseError(GLC_RESOURCE_ERROR);
73       FcPatternDestroy(pattern);
74       FcCharSetDestroy(This->charSet);
75       __glcFree(This);
76       return NULL;
77     }
78 
79     fontSet = FcFontList(inContext->config, pattern, objectSet);
80     FcObjectSetDestroy(objectSet);
81     FcPatternDestroy(pattern);
82     if (!fontSet) {
83       __glcRaiseError(GLC_RESOURCE_ERROR);
84       FcCharSetDestroy(This->charSet);
85       __glcFree(This);
86       return NULL;
87     }
88 
89     for (i = 0; i < fontSet->nfont; i++) {
90       FcChar8* family = NULL;
91       int fixed = 0;
92       FcChar8* foundry = NULL;
93       FcBool outline = FcFalse;
94       FcResult result = FcResultMatch;
95       FcBool equal = FcFalse;
96 
97       /* Check whether the glyphs are outlines */
98       result = FcPatternGetBool(fontSet->fonts[i], FC_OUTLINE, 0, &outline);
99       assert(result != FcResultTypeMismatch);
100 
101       if (!outline)
102 	continue;
103 
104       result = FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &family);
105       assert(result != FcResultTypeMismatch);
106       result = FcPatternGetString(fontSet->fonts[i], FC_FOUNDRY, 0, &foundry);
107       assert(result != FcResultTypeMismatch);
108       result = FcPatternGetInteger(fontSet->fonts[i], FC_SPACING, 0, &fixed);
109       assert(result != FcResultTypeMismatch);
110 
111       if (foundry)
112 	pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
113 				 FC_FOUNDRY, FcTypeString, foundry, FC_SPACING,
114 				 FcTypeInteger, fixed, NULL);
115       else
116 	pattern = FcPatternBuild(NULL, FC_FAMILY, FcTypeString, family,
117 				 FC_SPACING, FcTypeInteger, fixed, NULL);
118 
119       if (!pattern) {
120 	__glcRaiseError(GLC_RESOURCE_ERROR);
121 	FcCharSetDestroy(This->charSet);
122 	FcFontSetDestroy(fontSet);
123 	__glcFree(This);
124 	return NULL;
125       }
126 
127       equal = FcPatternEqual(pattern, inMaster->pattern);
128       FcPatternDestroy(pattern);
129       if (equal) {
130         FcCharSet* newCharSet = NULL;
131 
132         result = FcPatternGetCharSet(fontSet->fonts[i], FC_CHARSET, 0,
133 				     &charSet);
134         assert(result != FcResultTypeMismatch);
135 
136         newCharSet = FcCharSetUnion(This->charSet, charSet);
137 	if (!newCharSet) {
138           __glcRaiseError(GLC_RESOURCE_ERROR);
139           FcCharSetDestroy(This->charSet);
140           FcFontSetDestroy(fontSet);
141           __glcFree(This);
142           return NULL;
143 	}
144 
145 	FcCharSetDestroy(This->charSet);
146 	This->charSet = newCharSet;
147       }
148     }
149 
150     FcFontSetDestroy(fontSet);
151   }
152 
153   /* The array 'map' will contain the actual character map */
154   This->map = __glcArrayCreate(sizeof(__GLCcharMapElement));
155   if (!This->map) {
156     FcCharSetDestroy(This->charSet);
157     __glcFree(This);
158     return NULL;
159   }
160 
161   return This;
162 }
163 
164 
165 
166 /* Destructor of the object */
__glcCharMapDestroy(__GLCcharMap * This)167 void __glcCharMapDestroy(__GLCcharMap* This)
168 {
169   if (This->map)
170     __glcArrayDestroy(This->map);
171 
172   FcCharSetDestroy(This->charSet);
173 
174   __glcFree(This);
175 }
176 
177 
178 
179 /* Add a given character to the character map. Afterwards, the character map
180  * will associate the glyph 'inGlyph' to the Unicode codepoint 'inCode'.
181  */
__glcCharMapAddChar(__GLCcharMap * This,GLint inCode,__GLCglyph * inGlyph)182 void __glcCharMapAddChar(__GLCcharMap* This, GLint inCode, __GLCglyph* inGlyph)
183 {
184   __GLCcharMapElement* element = NULL;
185   __GLCcharMapElement* newElement = NULL;
186   int start = 0, middle = 0, end = 0;
187 
188   assert(This->map);
189   assert(GLC_ARRAY_DATA(This->map));
190   assert(inCode >= 0);
191 
192   /* Characters are stored by ascending order of their mapped code */
193   element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
194 
195   end = GLC_ARRAY_LENGTH(This->map) - 1;
196 
197   /* Parse the array by dichotomy to look for the place where to add the new
198    * character.
199    */
200   while (start <= end) {
201     middle = (start + end) >> 1;
202     /* If the character map already contains the new character then update the
203      * glyph then return.
204      */
205     if (element[middle].mappedCode == (GLCulong)inCode) {
206       element[middle].glyph = inGlyph;
207       return;
208     }
209     else if (element[middle].mappedCode > (GLCulong)inCode)
210       end = middle - 1;
211     else
212       start = middle + 1;
213   }
214 
215   /* If we have reached the end of the array then updated the rank 'middle'
216    * accordingly.
217    */
218   if ((end >= 0) && (element[middle].mappedCode < (GLCulong)inCode))
219     middle++;
220 
221   /* Insert the new character in the character map */
222   newElement = (__GLCcharMapElement*)__glcArrayInsertCell(This->map, middle, 1);
223   if (!newElement)
224     return;
225 
226   newElement->mappedCode = inCode;
227   newElement->glyph = inGlyph;
228   return;
229 }
230 
231 
232 
233 /* Remove a character from the character map */
__glcCharMapRemoveChar(__GLCcharMap * This,GLint inCode)234 void __glcCharMapRemoveChar(__GLCcharMap* This, GLint inCode)
235 {
236   __GLCcharMapElement* element = NULL;
237   int start = 0, middle = 0, end = 0;
238 
239   assert(This->map);
240   assert(GLC_ARRAY_DATA(This->map));
241   assert(inCode >= 0);
242 
243   /* Characters are stored by ascending order of their mapped code */
244   element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
245 
246   end = GLC_ARRAY_LENGTH(This->map) - 1;
247 
248   /* Parse the array by dichotomy to look for the place where to add the new
249    * character.
250    */
251   while (start <= end) {
252     middle = (start + end) >> 1;
253     /* When the character is found remove it from the array and return */
254     if (element[middle].mappedCode == (GLCulong)inCode) {
255       __glcArrayRemove(This->map, middle);
256       break;
257     }
258     else if (element[middle].mappedCode > (GLCulong)inCode)
259       end = middle - 1;
260     else
261       start = middle + 1;
262   }
263 }
264 
265 
266 
267 /* Get the Unicode character name of the character which codepoint is inCode.
268  * Note : since the character maps of the fonts can be altered, this function
269  * can return 'LATIN CAPITAL LETTER B' whereas inCode contained 65 (which is
270  * the Unicode code point of 'LATIN CAPITAL LETTER A').
271  */
__glcCharMapGetCharName(__GLCcharMap * This,GLint inCode)272 const GLCchar8* __glcCharMapGetCharName(__GLCcharMap* This, GLint inCode)
273 {
274   __GLCcharMapElement* element = NULL;
275   int start = 0, middle = 0, end = 0;
276   GLint code = 0;
277 
278   assert(This->map);
279   assert(GLC_ARRAY_DATA(This->map));
280   assert(inCode >= 0);
281 
282   /* Characters are stored by ascending order of their mapped code */
283   element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
284 
285   end = GLC_ARRAY_LENGTH(This->map) - 1;
286 
287   /* Parse the array by dichotomy to look for the Unicode codepoint that the
288    * request character maps to.
289    */
290   while (start <= end) {
291     middle = (start + end) >> 1;
292     if (element[middle].mappedCode == (GLCulong)inCode) {
293       code = element[middle].glyph->codepoint;
294       break;
295     }
296     else if (element[middle].mappedCode > (GLCulong)inCode)
297       end = middle - 1;
298     else
299       start = middle + 1;
300   }
301 
302   if (!code) {
303     if (FcCharSetHasChar(This->charSet, inCode))
304       code = inCode;
305     else
306       return NULL;
307   }
308 
309   return __glcNameFromCode(code);
310 }
311 
312 
313 
314 /* Get the glyph corresponding to codepoint 'inCode' */
__glcCharMapGetGlyph(__GLCcharMap * This,GLint inCode)315 __GLCglyph* __glcCharMapGetGlyph(__GLCcharMap* This, GLint inCode)
316 {
317   __GLCcharMapElement* element = NULL;
318   int start = 0, middle = 0, end = 0;
319 
320   assert(This->map);
321   assert(GLC_ARRAY_DATA(This->map));
322   assert(inCode >= 0);
323 
324   /* Characters are stored by ascending order of their mapped code */
325   element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
326 
327   end = GLC_ARRAY_LENGTH(This->map) - 1;
328 
329   /* Parse the array by dichotomy to find the glyph of the requested
330    * character.
331    */
332   while (start <= end) {
333     middle = (start + end) >> 1;
334     if (element[middle].mappedCode == (GLCulong)inCode)
335       /* When the character is found return the corresponding glyph */
336       return element[middle].glyph;
337     else if (element[middle].mappedCode > (GLCulong)inCode)
338       end = middle - 1;
339     else
340       start = middle + 1;
341   }
342 
343   /* No glyph has been defined yet for the requested character */
344   return NULL;
345 }
346 
347 
348 
349 /* Check if a character is in the character map */
__glcCharMapHasChar(__GLCcharMap * This,GLint inCode)350 GLboolean __glcCharMapHasChar(__GLCcharMap* This, GLint inCode)
351 {
352   __GLCcharMapElement* element = NULL;
353   int start = 0, middle = 0, end = 0;
354 
355   assert(This->map);
356   assert(GLC_ARRAY_DATA(This->map));
357   assert(inCode >= 0);
358 
359   /* Characters are stored by ascending order of their mapped code */
360   element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
361 
362   end = GLC_ARRAY_LENGTH(This->map) - 1;
363 
364   /* Parse the array by dichotomy to find the requested character. */
365   while (start <= end) {
366     middle = (start + end) >> 1;
367     /* The character has been found : return GL_TRUE */
368     if (element[middle].mappedCode == (GLCulong)inCode)
369       return GL_TRUE;
370     else if (element[middle].mappedCode > (GLCulong)inCode)
371       end = middle - 1;
372     else
373       start = middle + 1;
374   }
375 
376   /* Check if the character identified by inCode exists in the font */
377   return FcCharSetHasChar(This->charSet, inCode);
378 }
379 
380 
381 
382 /* This function counts the number of bits that are set in c1
383  * Copied from Keith Packard's fontconfig
384  */
__glcCharSetPopCount(GLCchar32 c1)385 static GLCchar32 __glcCharSetPopCount(GLCchar32 c1)
386 {
387   /* hackmem 169 */
388   GLCchar32    c2 = (c1 >> 1) & 033333333333;
389   c2 = c1 - c2 - ((c2 >> 1) & 033333333333);
390   return (((c2 + (c2 >> 3)) & 030707070707) % 077);
391 }
392 
393 
394 
395 /* Get the name of the character which is stored at rank 'inIndex' in the
396  * FcCharSet of the face.
397  */
__glcCharMapGetCharNameByIndex(__GLCcharMap * This,GLint inIndex)398 const GLCchar8* __glcCharMapGetCharNameByIndex(__GLCcharMap* This,
399 					       GLint inIndex)
400 {
401   int i = 0;
402   int j = 0;
403 
404   /* In Fontconfig the map in FcCharSet is organized as an array of integers.
405    * Each integer corresponds to a page of 32 characters (since it uses 32 bits
406    * integer). If a bit is set then character is in the character map otherwise
407    * it is not.
408    * In order not to store pages of 0's, the character map begins at the
409    * character which codepoint is 'base'.
410    * Pages are also gathered in blocks of 'FC_CHARSET_MAP_SIZE' pages in order
411    * to prevent Fontconfig to store heaps of 0's if the character codes are
412    * sparsed.
413    *
414    * The codepoint of a character located at bit 'j' of page 'i' is :
415    * 'base + (i << 5) + j'.
416    */
417   GLCchar32 map[FC_CHARSET_MAP_SIZE];
418   GLCchar32 next = 0;
419   GLCchar32 base = FcCharSetFirstPage(This->charSet, map, &next);
420   GLCchar32 count = 0;
421   GLCchar32 value = 0;
422 
423   assert(inIndex >= 0);
424 
425   do {
426     /* Parse the pages in FcCharSet */
427     for (i = 0; i < FC_CHARSET_MAP_SIZE; i++) {
428       /* Get the number of character located in the current page */
429       value = __glcCharSetPopCount(map[i]);
430 
431       /* Check if the character we are looking for is in the current page */
432       if (count + value >= (GLCchar32)inIndex + 1) {
433 	for (j = 0; j < 32; j++) {
434 	  /* Parse the page bit by bit */
435 	  if ((map[i] >> j) & 1) count++; /* A character is set at bit j */
436 	  /* Check if we have reached the rank inIndex */
437 	  if (count == (GLCchar32)inIndex + 1) {
438 	    /* Get the character name */
439 	    return __glcNameFromCode(base + (i << 5) + j);
440 	  }
441 	}
442       }
443       /* Add the number of characters of the current page to the count and
444        * check the next page.
445        */
446       count += value;
447     }
448     /* The current block is finished, check the next one */
449     base = FcCharSetNextPage(This->charSet, map, &next);
450   } while (base != FC_CHARSET_DONE);
451 
452   /* The character has not been found */
453   __glcRaiseError(GLC_PARAMETER_ERROR);
454   return GLC_NONE;
455 }
456 
457 
458 
459 /* Return the number of characters in the character map */
__glcCharMapGetCount(__GLCcharMap * This)460 GLint __glcCharMapGetCount(__GLCcharMap* This)
461 {
462   return FcCharSetCount(This->charSet);
463 }
464 
465 
466 
467 /* Get the maximum mapped code of a character set */
__glcCharMapGetMaxMappedCode(__GLCcharMap * This)468 GLint __glcCharMapGetMaxMappedCode(__GLCcharMap* This)
469 {
470   GLCchar32 base = 0;
471   GLCchar32 next = 0;
472   GLCchar32 prev_base = 0;
473   GLCchar32 map[FC_CHARSET_MAP_SIZE];
474   int i = 0, j = 0;
475   GLCulong maxMappedCode = 0;
476   __GLCcharMapElement* element = NULL;
477   int length = 0;
478 
479   assert(This->map);
480   assert(GLC_ARRAY_DATA(This->map));
481 
482   /* Look for the last block of pages of the FcCharSet structure */
483   base = FcCharSetFirstPage(This->charSet, map, &next);
484   assert(base != FC_CHARSET_DONE);
485 
486   do {
487     prev_base = base;
488     base = FcCharSetNextPage(This->charSet, map, &next);
489   } while (base != FC_CHARSET_DONE);
490 
491   /* Parse the pages in descending order to find the last page that contains
492    * one character.
493    */
494   for (i = FC_CHARSET_MAP_SIZE - 1; i >= 0; i--)
495     if (map[i]) break;
496 
497   /* If the map contains no char then something went wrong... */
498   assert(i >= 0);
499 
500   /* Parse the bits of the last page in descending order to find the last
501    * character of the page
502    */
503   for (j = 31; j >= 0; j--)
504     if ((map[i] >> j) & 1) break;
505 
506   /* Calculate the max mapped code */
507   maxMappedCode = prev_base + (i << 5) + j;
508 
509   /* Check that a code greater than the one found in the FcCharSet is not
510    * stored in the array 'map'.
511    */
512   element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
513   length = GLC_ARRAY_LENGTH(This->map);
514 
515   /* Return the greater of the code of both the FcCharSet and the array 'map'*/
516   if (length)
517     return element[length-1].mappedCode > maxMappedCode ?
518       element[length-1].mappedCode : maxMappedCode;
519   else
520     return maxMappedCode;
521 }
522 
523 
524 
525 /* Get the minimum mapped code of a character set */
__glcCharMapGetMinMappedCode(__GLCcharMap * This)526 GLint __glcCharMapGetMinMappedCode(__GLCcharMap* This)
527 {
528   GLCchar32 base = 0;
529   GLCchar32 next = 0;
530   GLCchar32 map[FC_CHARSET_MAP_SIZE];
531   int i = 0, j = 0;
532   GLCulong minMappedCode = 0xffffffff;
533   __GLCcharMapElement* element = NULL;
534   int length = 0;
535 
536   assert(This->map);
537   assert(GLC_ARRAY_DATA(This->map));
538 
539   /* Get the first block of pages of the FcCharSet structure */
540   base = FcCharSetFirstPage(This->charSet, map, &next);
541   assert(base != FC_CHARSET_DONE);
542 
543   /* Parse the pages in ascending order to find the first page that contains
544    * one character.
545    */
546   for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
547     if (map[i]) break;
548 
549   /* If the map contains no char then something went wrong... */
550   assert(i >= 0);
551 
552   /* Parse the bits of the first page in ascending order to find the first
553    * character of the page
554    */
555   for (j = 0; j < 32; j++)
556     if ((map[i] >> j) & 1) break;
557   minMappedCode = base + (i << 5) + j;
558 
559   /* Check that a code lower than the one found in the FcCharSet is not
560    * stored in the array 'map'.
561    */
562   element = (__GLCcharMapElement*)GLC_ARRAY_DATA(This->map);
563   length = GLC_ARRAY_LENGTH(This->map);
564 
565   /* Return the lower of the code of both the FcCharSet and the array 'map'*/
566   if (length > 0)
567     return element[0].mappedCode < minMappedCode ?
568       element[0].mappedCode : minMappedCode;
569   else
570     return minMappedCode;
571 }
572