1 /***************************************************************************/
2 /* */
3 /* ttcmap.c */
4 /* */
5 /* TrueType character mapping table (cmap) support (body). */
6 /* */
7 /* Copyright 1996-2001 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
9 /* */
10 /* This file is part of the FreeType project, and may only be used, */
11 /* modified, and distributed under the terms of the FreeType project */
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13 /* this file you indicate that you have read the license and */
14 /* understand and accept it fully. */
15 /* */
16 /***************************************************************************/
19 #include <ft2build.h>
20 #include FT_INTERNAL_DEBUG_H
21 #include "ttload.h"
22 #include "ttcmap.h"
24 #include "sferrors.h"
27 /*************************************************************************/
28 /* */
29 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
30 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
31 /* messages during execution. */
32 /* */
33 #undef FT_COMPONENT
34 #define FT_COMPONENT trace_ttcmap
37 FT_CALLBACK_DEF( FT_UInt )
38 code_to_index0( TT_CMapTable* charmap,
39 FT_ULong char_code );
41 FT_CALLBACK_DEF( FT_UInt )
42 code_to_index2( TT_CMapTable* charmap,
43 FT_ULong char_code );
45 FT_CALLBACK_DEF( FT_UInt )
46 code_to_index4( TT_CMapTable* charmap,
47 FT_ULong char_code );
49 FT_CALLBACK_DEF( FT_UInt )
50 code_to_index6( TT_CMapTable* charmap,
51 FT_ULong char_code );
53 FT_CALLBACK_DEF( FT_UInt )
54 code_to_index8_12( TT_CMapTable* charmap,
55 FT_ULong char_code );
57 FT_CALLBACK_DEF( FT_UInt )
58 code_to_index10( TT_CMapTable* charmap,
59 FT_ULong char_code );
62 /*************************************************************************/
63 /* */
64 /* <Function> */
65 /* TT_CharMap_Load */
66 /* */
67 /* <Description> */
68 /* Loads a given TrueType character map into memory. */
69 /* */
70 /* <Input> */
71 /* face :: A handle to the parent face object. */
72 /* stream :: A handle to the current stream object. */
73 /* */
74 /* <InOut> */
75 /* table :: A pointer to a cmap object. */
76 /* */
77 /* <Return> */
78 /* FreeType error code. 0 means success. */
79 /* */
80 /* <Note> */
81 /* The function assumes that the stream is already in use (i.e., */
82 /* opened). In case of error, all partially allocated tables are */
83 /* released. */
84 /* */
85 FT_LOCAL_DEF FT_Error
86 TT_CharMap_Load( TT_Face face,
87 TT_CMapTable* cmap,
88 FT_Stream stream )
89 {
90 FT_Error error;
91 FT_Memory memory;
92 FT_UShort num_SH, num_Seg, i;
93 FT_ULong j, n;
95 FT_UShort u, l;
97 TT_CMap0* cmap0;
98 TT_CMap2* cmap2;
99 TT_CMap4* cmap4;
100 TT_CMap6* cmap6;
101 TT_CMap8_12* cmap8_12;
102 TT_CMap10* cmap10;
104 TT_CMap2SubHeader* cmap2sub;
105 TT_CMap4Segment* segments;
106 TT_CMapGroup* groups;
109 if ( cmap->loaded )
110 return SFNT_Err_Ok;
112 memory = stream->memory;
114 if ( FILE_Seek( cmap->offset ) )
115 return error;
117 switch ( cmap->format )
118 {
119 case 0:
120 cmap0 = &cmap->c.cmap0;
122 if ( READ_UShort( cmap0->language ) ||
123 ALLOC( cmap0->glyphIdArray, 256L ) ||
124 FILE_Read( cmap0->glyphIdArray, 256L ) )
125 goto Fail;
127 cmap->get_index = code_to_index0;
128 break;
130 case 2:
131 num_SH = 0;
132 cmap2 = &cmap->c.cmap2;
134 /* allocate subheader keys */
136 if ( ALLOC_ARRAY( cmap2->subHeaderKeys, 256, FT_UShort ) ||
137 ACCESS_Frame( 2L + 512L ) )
138 goto Fail;
140 cmap2->language = GET_UShort();
142 for ( i = 0; i < 256; i++ )
143 {
144 u = (FT_UShort)( GET_UShort() / 8 );
145 cmap2->subHeaderKeys[i] = u;
147 if ( num_SH < u )
148 num_SH = u;
149 }
151 FORGET_Frame();
153 /* load subheaders */
155 cmap2->numGlyphId = l = (FT_UShort)(
156 ( ( cmap->length - 2L * ( 256 + 3 ) - num_SH * 8L ) & 0xFFFF ) / 2 );
158 if ( ALLOC_ARRAY( cmap2->subHeaders,
159 num_SH + 1,
160 TT_CMap2SubHeader ) ||
161 ACCESS_Frame( ( num_SH + 1 ) * 8L ) )
162 {
163 FREE( cmap2->subHeaderKeys );
164 goto Fail;
165 }
167 cmap2sub = cmap2->subHeaders;
169 for ( i = 0; i <= num_SH; i++ )
170 {
171 cmap2sub->firstCode = GET_UShort();
172 cmap2sub->entryCount = GET_UShort();
173 cmap2sub->idDelta = GET_Short();
174 /* we apply the location offset immediately */
175 cmap2sub->idRangeOffset = (FT_UShort)(
176 GET_UShort() - ( num_SH - i ) * 8 - 2 );
178 cmap2sub++;
179 }
181 FORGET_Frame();
183 /* load glyph IDs */
185 if ( ALLOC_ARRAY( cmap2->glyphIdArray, l, FT_UShort ) ||
186 ACCESS_Frame( l * 2L ) )
187 {
188 FREE( cmap2->subHeaders );
189 FREE( cmap2->subHeaderKeys );
190 goto Fail;
191 }
193 for ( i = 0; i < l; i++ )
194 cmap2->glyphIdArray[i] = GET_UShort();
196 FORGET_Frame();
198 cmap->get_index = code_to_index2;
199 break;
201 case 4:
202 cmap4 = &cmap->c.cmap4;
204 /* load header */
206 if ( ACCESS_Frame( 10L ) )
207 goto Fail;
209 cmap4->language = GET_UShort();
210 cmap4->segCountX2 = GET_UShort();
211 cmap4->searchRange = GET_UShort();
212 cmap4->entrySelector = GET_UShort();
213 cmap4->rangeShift = GET_UShort();
215 num_Seg = (FT_UShort)( cmap4->segCountX2 / 2 );
217 FORGET_Frame();
219 /* load segments */
221 if ( ALLOC_ARRAY( cmap4->segments,
222 num_Seg,
223 TT_CMap4Segment ) ||
224 ACCESS_Frame( ( num_Seg * 4 + 1 ) * 2L ) )
225 goto Fail;
227 segments = cmap4->segments;
229 for ( i = 0; i < num_Seg; i++ )
230 segments[i].endCount = GET_UShort();
232 (void)GET_UShort();
234 for ( i = 0; i < num_Seg; i++ )
235 segments[i].startCount = GET_UShort();
237 for ( i = 0; i < num_Seg; i++ )
238 segments[i].idDelta = GET_Short();
240 for ( i = 0; i < num_Seg; i++ )
241 segments[i].idRangeOffset = GET_UShort();
243 FORGET_Frame();
245 cmap4->numGlyphId = l = (FT_UShort)(
246 ( ( cmap->length - ( 16L + 8L * num_Seg ) ) & 0xFFFF ) / 2 );
248 /* load IDs */
250 if ( ALLOC_ARRAY( cmap4->glyphIdArray, l, FT_UShort ) ||
251 ACCESS_Frame( l * 2L ) )
252 {
253 FREE( cmap4->segments );
254 goto Fail;
255 }
257 for ( i = 0; i < l; i++ )
258 cmap4->glyphIdArray[i] = GET_UShort();
260 FORGET_Frame();
262 cmap4->last_segment = cmap4->segments;
264 cmap->get_index = code_to_index4;
265 break;
267 case 6:
268 cmap6 = &cmap->c.cmap6;
270 if ( ACCESS_Frame( 6L ) )
271 goto Fail;
273 cmap6->language = GET_UShort();
274 cmap6->firstCode = GET_UShort();
275 cmap6->entryCount = GET_UShort();
277 FORGET_Frame();
279 l = cmap6->entryCount;
281 if ( ALLOC_ARRAY( cmap6->glyphIdArray, l, FT_Short ) ||
282 ACCESS_Frame( l * 2L ) )
283 goto Fail;
285 for ( i = 0; i < l; i++ )
286 cmap6->glyphIdArray[i] = GET_UShort();
288 FORGET_Frame();
289 cmap->get_index = code_to_index6;
290 break;
292 case 8:
293 case 12:
294 cmap8_12 = &cmap->c.cmap8_12;
296 if ( ACCESS_Frame( 8L ) )
297 goto Fail;
299 cmap->length = GET_ULong();
300 cmap8_12->language = GET_ULong();
302 FORGET_Frame();
304 if ( cmap->format == 8 )
305 if ( FILE_Skip( 8192L ) )
306 goto Fail;
308 if ( READ_ULong( cmap8_12->nGroups ) )
309 goto Fail;
311 n = cmap8_12->nGroups;
313 if ( ALLOC_ARRAY( cmap8_12->groups, n, TT_CMapGroup ) ||
314 ACCESS_Frame( n * 3 * 4L ) )
315 goto Fail;
317 groups = cmap8_12->groups;
319 for ( j = 0; j < n; j++ )
320 {
321 groups[j].startCharCode = GET_ULong();
322 groups[j].endCharCode = GET_ULong();
323 groups[j].startGlyphID = GET_ULong();
324 }
326 FORGET_Frame();
328 cmap8_12->last_group = cmap8_12->groups;
330 cmap->get_index = code_to_index8_12;
331 break;
333 case 10:
334 cmap10 = &cmap->c.cmap10;
336 if ( ACCESS_Frame( 16L ) )
337 goto Fail;
339 cmap->length = GET_ULong();
340 cmap10->language = GET_ULong();
341 cmap10->startCharCode = GET_ULong();
342 cmap10->numChars = GET_ULong();
344 FORGET_Frame();
346 n = cmap10->numChars;
348 if ( ALLOC_ARRAY( cmap10->glyphs, n, FT_Short ) ||
349 ACCESS_Frame( n * 2L ) )
350 goto Fail;
352 for ( j = 0; j < n; j++ )
353 cmap10->glyphs[j] = GET_UShort();
355 FORGET_Frame();
356 cmap->get_index = code_to_index10;
357 break;
359 default: /* corrupt character mapping table */
360 return SFNT_Err_Invalid_CharMap_Format;
362 }
364 return SFNT_Err_Ok;
366 Fail:
367 TT_CharMap_Free( face, cmap );
368 return error;
369 }
372 /*************************************************************************/
373 /* */
374 /* <Function> */
375 /* TT_CharMap_Free */
376 /* */
377 /* <Description> */
378 /* Destroys a character mapping table. */
379 /* */
380 /* <Input> */
381 /* face :: A handle to the parent face object. */
382 /* cmap :: A handle to a cmap object. */
383 /* */
384 /* <Return> */
385 /* FreeType error code. 0 means success. */
386 /* */
387 FT_LOCAL_DEF FT_Error
388 TT_CharMap_Free( TT_Face face,
389 TT_CMapTable* cmap )
390 {
391 FT_Memory memory;
394 if ( !cmap )
395 return SFNT_Err_Ok;
397 memory = face->root.driver->root.memory;
399 switch ( cmap->format )
400 {
401 case 0:
402 FREE( cmap->c.cmap0.glyphIdArray );
403 break;
405 case 2:
406 FREE( cmap->c.cmap2.subHeaderKeys );
407 FREE( cmap->c.cmap2.subHeaders );
408 FREE( cmap->c.cmap2.glyphIdArray );
409 break;
411 case 4:
412 FREE( cmap->c.cmap4.segments );
413 FREE( cmap->c.cmap4.glyphIdArray );
414 cmap->c.cmap4.segCountX2 = 0;
415 break;
417 case 6:
418 FREE( cmap->c.cmap6.glyphIdArray );
419 cmap->c.cmap6.entryCount = 0;
420 break;
422 case 8:
423 case 12:
424 FREE( cmap->c.cmap8_12.groups );
425 cmap->c.cmap8_12.nGroups = 0;
426 break;
428 case 10:
429 FREE( cmap->c.cmap10.glyphs );
430 cmap->c.cmap10.numChars = 0;
431 break;
433 default:
434 /* invalid table format, do nothing */
435 ;
436 }
438 cmap->loaded = FALSE;
439 return SFNT_Err_Ok;
440 }
443 /*************************************************************************/
444 /* */
445 /* <Function> */
446 /* code_to_index0 */
447 /* */
448 /* <Description> */
449 /* Converts the character code into a glyph index. Uses format 0. */
450 /* `charCode' must be in the range 0x00-0xFF (otherwise 0 is */
451 /* returned). */
452 /* */
453 /* <Input> */
454 /* charCode :: The wanted character code. */
455 /* cmap0 :: A pointer to a cmap table in format 0. */
456 /* */
457 /* <Return> */
458 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
459 /* */
460 FT_CALLBACK_DEF( FT_UInt )
461 code_to_index0( TT_CMapTable* cmap,
462 FT_ULong charCode )
463 {
464 TT_CMap0* cmap0 = &cmap->c.cmap0;
467 return ( charCode <= 0xFF ? cmap0->glyphIdArray[charCode] : 0 );
468 }
471 /*************************************************************************/
472 /* */
473 /* <Function> */
474 /* code_to_index2 */
475 /* */
476 /* <Description> */
477 /* Converts the character code into a glyph index. Uses format 2. */
478 /* */
479 /* <Input> */
480 /* charCode :: The wanted character code. */
481 /* cmap2 :: A pointer to a cmap table in format 2. */
482 /* */
483 /* <Return> */
484 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
485 /* */
486 FT_CALLBACK_DEF( FT_UInt )
487 code_to_index2( TT_CMapTable* cmap,
488 FT_ULong charCode )
489 {
490 FT_UInt result, index1, offset;
491 FT_UInt char_lo;
492 FT_ULong char_hi;
493 TT_CMap2SubHeader* sh2;
494 TT_CMap2* cmap2;
497 cmap2 = &cmap->c.cmap2;
498 result = 0;
499 char_lo = (FT_UInt)( charCode & 0xFF );
500 char_hi = charCode >> 8;
502 if ( char_hi == 0 )
503 {
504 /* an 8-bit character code -- we use the subHeader 0 in this case */
505 /* to test whether the character code is in the charmap */
506 index1 = cmap2->subHeaderKeys[char_lo];
507 if ( index1 != 0 )
508 return 0;
509 }
510 else
511 {
512 /* a 16-bit character code */
513 index1 = cmap2->subHeaderKeys[char_hi & 0xFF];
514 if ( index1 == 0 )
515 return 0;
516 }
518 sh2 = cmap2->subHeaders + index1;
519 char_lo -= sh2->firstCode;
521 if ( char_lo < (FT_UInt)sh2->entryCount )
522 {
523 offset = sh2->idRangeOffset / 2 + char_lo;
524 if ( offset < (FT_UInt)cmap2->numGlyphId )
525 {
526 result = cmap2->glyphIdArray[offset];
527 if ( result )
528 result = ( result + sh2->idDelta ) & 0xFFFF;
529 }
530 }
532 return result;
533 }
536 /*************************************************************************/
537 /* */
538 /* <Function> */
539 /* code_to_index4 */
540 /* */
541 /* <Description> */
542 /* Converts the character code into a glyph index. Uses format 4. */
543 /* */
544 /* <Input> */
545 /* charCode :: The wanted character code. */
546 /* cmap4 :: A pointer to a cmap table in format 4. */
547 /* */
548 /* <Return> */
549 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
550 /* */
551 FT_CALLBACK_DEF( FT_UInt )
552 code_to_index4( TT_CMapTable* cmap,
553 FT_ULong charCode )
554 {
555 FT_UInt result, index1, segCount;
556 TT_CMap4* cmap4;
557 TT_CMap4Segment *seg4, *limit;
560 cmap4 = &cmap->c.cmap4;
561 result = 0;
562 segCount = cmap4->segCountX2 / 2;
563 limit = cmap4->segments + segCount;
565 /* first, check against the last used segment */
567 seg4 = cmap4->last_segment;
569 /* the following is equivalent to performing two tests, as in */
570 /* */
571 /* if ( charCode >= seg4->startCount && charCode <= seg4->endCount ) */
572 /* */
573 /* This is a bit strange, but it is faster, and the idea behind the */
574 /* cache is to significantly speed up charcode to glyph index */
575 /* conversion. */
577 if ( (FT_ULong)( charCode - seg4->startCount ) <
578 (FT_ULong)( seg4->endCount - seg4->startCount ) )
579 goto Found1;
581 for ( seg4 = cmap4->segments; seg4 < limit; seg4++ )
582 {
583 /* the ranges are sorted in increasing order. If we are out of */
584 /* the range here, the char code isn't in the charmap, so exit. */
586 if ( charCode > (FT_UInt)seg4->endCount )
587 continue;
589 if ( charCode >= (FT_UInt)seg4->startCount )
590 goto Found;
591 }
592 return 0;
594 Found:
595 cmap4->last_segment = seg4;
597 Found1:
598 /* if the idRangeOffset is 0, we can compute the glyph index */
599 /* directly */
601 if ( seg4->idRangeOffset == 0 )
602 result = ( charCode + seg4->idDelta ) & 0xFFFF;
603 else
604 {
605 /* otherwise, we must use the glyphIdArray to do it */
606 index1 = (FT_UInt)( seg4->idRangeOffset / 2
607 + ( charCode - seg4->startCount )
608 + ( seg4 - cmap4->segments )
609 - segCount );
611 if ( index1 < (FT_UInt)cmap4->numGlyphId &&
612 cmap4->glyphIdArray[index1] != 0 )
613 result = ( cmap4->glyphIdArray[index1] + seg4->idDelta ) & 0xFFFF;
614 }
616 return result;
617 }
620 /*************************************************************************/
621 /* */
622 /* <Function> */
623 /* code_to_index6 */
624 /* */
625 /* <Description> */
626 /* Converts the character code into a glyph index. Uses format 6. */
627 /* */
628 /* <Input> */
629 /* charCode :: The wanted character code. */
630 /* cmap6 :: A pointer to a cmap table in format 6. */
631 /* */
632 /* <Return> */
633 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
634 /* */
635 FT_CALLBACK_DEF( FT_UInt )
636 code_to_index6( TT_CMapTable* cmap,
637 FT_ULong charCode )
638 {
639 TT_CMap6* cmap6;
640 FT_UInt result = 0;
643 cmap6 = &cmap->c.cmap6;
644 charCode -= cmap6->firstCode;
646 if ( charCode < (FT_UInt)cmap6->entryCount )
647 result = cmap6->glyphIdArray[charCode];
649 return result;
650 }
653 /*************************************************************************/
654 /* */
655 /* <Function> */
656 /* code_to_index8_12 */
657 /* */
658 /* <Description> */
659 /* Converts the (possibly 32bit) character code into a glyph index. */
660 /* Uses format 8 or 12. */
661 /* */
662 /* <Input> */
663 /* charCode :: The wanted character code. */
664 /* cmap8_12 :: A pointer to a cmap table in format 8 or 12. */
665 /* */
666 /* <Return> */
667 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
668 /* */
669 FT_CALLBACK_DEF( FT_UInt )
670 code_to_index8_12( TT_CMapTable* cmap,
671 FT_ULong charCode )
672 {
673 TT_CMap8_12* cmap8_12;
674 TT_CMapGroup *group, *limit;
677 cmap8_12 = &cmap->c.cmap8_12;
678 limit = cmap8_12->groups + cmap8_12->nGroups;
680 /* first, check against the last used group */
682 group = cmap8_12->last_group;
684 /* the following is equivalent to performing two tests, as in */
685 /* */
686 /* if ( charCode >= group->startCharCode && */
687 /* charCode <= group->endCharCode ) */
688 /* */
689 /* This is a bit strange, but it is faster, and the idea behind the */
690 /* cache is to significantly speed up charcode to glyph index */
691 /* conversion. */
693 if ( (FT_ULong)( charCode - group->startCharCode ) <
694 (FT_ULong)( group->endCharCode - group->startCharCode ) )
695 goto Found1;
697 for ( group = cmap8_12->groups; group < limit; group++ )
698 {
699 /* the ranges are sorted in increasing order. If we are out of */
700 /* the range here, the char code isn't in the charmap, so exit. */
702 if ( charCode > group->endCharCode )
703 continue;
705 if ( charCode >= group->startCharCode )
706 goto Found;
707 }
708 return 0;
710 Found:
711 cmap8_12->last_group = group;
713 Found1:
714 return group->startGlyphID + (FT_UInt)( charCode - group->startCharCode );
715 }
718 /*************************************************************************/
719 /* */
720 /* <Function> */
721 /* code_to_index10 */
722 /* */
723 /* <Description> */
724 /* Converts the (possibly 32bit) character code into a glyph index. */
725 /* Uses format 10. */
726 /* */
727 /* <Input> */
728 /* charCode :: The wanted character code. */
729 /* cmap10 :: A pointer to a cmap table in format 10. */
730 /* */
731 /* <Return> */
732 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
733 /* */
734 FT_CALLBACK_DEF( FT_UInt )
735 code_to_index10( TT_CMapTable* cmap,
736 FT_ULong charCode )
737 {
738 TT_CMap10* cmap10;
739 FT_UInt result = 0;
742 cmap10 = &cmap->c.cmap10;
743 charCode -= cmap10->startCharCode;
745 /* the overflow trick for comparison works here also since the number */
746 /* of glyphs (even if numChars is specified as ULong in the specs) in */
747 /* an OpenType font is limited to 64k */
749 if ( charCode < cmap10->numChars )
750 result = cmap10->glyphs[charCode];
752 return result;
753 }
756 /* END */