1 /***************************************************************************/
5 /* TrueType character mapping table (cmap) support (body). */
7 /* Copyright 1996-2000 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
19 #include <freetype/internal/ftdebug.h>
20 #include <freetype/internal/tterrors.h>
23 #ifdef FT_FLAT_COMPILE
30 #include <sfnt/ttload.h>
31 #include <sfnt/ttcmap.h>
36 /*************************************************************************/
38 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
39 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
40 /* messages during execution. */
43 #define FT_COMPONENT trace_ttcmap
46 static FT_UInt
code_to_index0( TT_CMapTable
* charmap
,
48 static FT_UInt
code_to_index2( TT_CMapTable
* charmap
,
50 static FT_UInt
code_to_index4( TT_CMapTable
* charmap
,
52 static FT_UInt
code_to_index6( TT_CMapTable
* charmap
,
56 /*************************************************************************/
62 /* Loads a given TrueType character map into memory. */
65 /* face :: A handle to the parent face object. */
66 /* stream :: A handle to the current stream object. */
69 /* table :: A pointer to a cmap object. */
72 /* FreeType error code. 0 means success. */
75 /* The function assumes that the stream is already in use (i.e., */
76 /* opened). In case of error, all partially allocated tables are */
80 FT_Error
TT_CharMap_Load( TT_Face face
,
86 FT_UShort num_SH
, num_Seg
, i
;
95 TT_CMap2SubHeader
* cmap2sub
;
96 TT_CMap4Segment
* segments
;
102 memory
= stream
->memory
;
104 if ( FILE_Seek( cmap
->offset
) )
107 switch ( cmap
->format
)
110 cmap0
= &cmap
->c
.cmap0
;
112 if ( ALLOC( cmap0
->glyphIdArray
, 256L ) ||
113 FILE_Read( cmap0
->glyphIdArray
, 256L ) )
116 cmap
->get_index
= code_to_index0
;
121 cmap2
= &cmap
->c
.cmap2
;
123 /* allocate subheader keys */
125 if ( ALLOC_ARRAY( cmap2
->subHeaderKeys
, 256, FT_UShort
) ||
126 ACCESS_Frame( 512L ) )
129 for ( i
= 0; i
< 256; i
++ )
131 u
= GET_UShort() / 8;
132 cmap2
->subHeaderKeys
[i
] = u
;
140 /* load subheaders */
142 cmap2
->numGlyphId
= l
=
143 ( ( cmap
->length
- 2L * ( 256 + 3 ) - num_SH
* 8L ) & 0xFFFF ) / 2;
145 if ( ALLOC_ARRAY( cmap2
->subHeaders
,
147 TT_CMap2SubHeader
) ||
148 ACCESS_Frame( ( num_SH
+ 1 ) * 8L ) )
151 cmap2sub
= cmap2
->subHeaders
;
153 for ( i
= 0; i
<= num_SH
; i
++ )
155 cmap2sub
->firstCode
= GET_UShort();
156 cmap2sub
->entryCount
= GET_UShort();
157 cmap2sub
->idDelta
= GET_Short();
158 /* we apply the location offset immediately */
159 cmap2sub
->idRangeOffset
= GET_UShort() - ( num_SH
- i
) * 8 - 2;
168 if ( ALLOC_ARRAY( cmap2
->glyphIdArray
, l
, FT_UShort
) ||
169 ACCESS_Frame( l
* 2L ) )
172 for ( i
= 0; i
< l
; i
++ )
173 cmap2
->glyphIdArray
[i
] = GET_UShort();
177 cmap
->get_index
= code_to_index2
;
181 cmap4
= &cmap
->c
.cmap4
;
185 if ( ACCESS_Frame( 8L ) )
188 cmap4
->segCountX2
= GET_UShort();
189 cmap4
->searchRange
= GET_UShort();
190 cmap4
->entrySelector
= GET_UShort();
191 cmap4
->rangeShift
= GET_UShort();
193 num_Seg
= cmap4
->segCountX2
/ 2;
199 if ( ALLOC_ARRAY( cmap4
->segments
,
202 ACCESS_Frame( ( num_Seg
* 4 + 1 ) * 2L ) )
205 segments
= cmap4
->segments
;
207 for ( i
= 0; i
< num_Seg
; i
++ )
208 segments
[i
].endCount
= GET_UShort();
212 for ( i
= 0; i
< num_Seg
; i
++ )
213 segments
[i
].startCount
= GET_UShort();
215 for ( i
= 0; i
< num_Seg
; i
++ )
216 segments
[i
].idDelta
= GET_Short();
218 for ( i
= 0; i
< num_Seg
; i
++ )
219 segments
[i
].idRangeOffset
= GET_UShort();
223 cmap4
->numGlyphId
= l
=
224 ( ( cmap
->length
- ( 16L + 8L * num_Seg
) ) & 0xFFFF ) / 2;
228 if ( ALLOC_ARRAY( cmap4
->glyphIdArray
, l
, FT_UShort
) ||
229 ACCESS_Frame( l
* 2L ) )
232 for ( i
= 0; i
< l
; i
++ )
233 cmap4
->glyphIdArray
[i
] = GET_UShort();
237 cmap
->get_index
= code_to_index4
;
239 cmap4
->last_segment
= cmap4
->segments
;
243 cmap6
= &cmap
->c
.cmap6
;
245 if ( ACCESS_Frame( 4L ) )
248 cmap6
->firstCode
= GET_UShort();
249 cmap6
->entryCount
= GET_UShort();
253 l
= cmap6
->entryCount
;
255 if ( ALLOC_ARRAY( cmap6
->glyphIdArray
,
258 ACCESS_Frame( l
* 2L ) )
261 for ( i
= 0; i
< l
; i
++ )
262 cmap6
->glyphIdArray
[i
] = GET_UShort();
265 cmap
->get_index
= code_to_index6
;
268 default: /* corrupt character mapping table */
269 return TT_Err_Invalid_CharMap_Format
;
276 TT_CharMap_Free( face
, cmap
);
281 /*************************************************************************/
284 /* TT_CharMap_Free */
287 /* Destroys a character mapping table. */
290 /* face :: A handle to the parent face object. */
291 /* cmap :: A handle to a cmap object. */
294 /* FreeType error code. 0 means success. */
297 FT_Error
TT_CharMap_Free( TT_Face face
,
306 memory
= face
->root
.driver
->root
.memory
;
308 switch ( cmap
->format
)
311 FREE( cmap
->c
.cmap0
.glyphIdArray
);
315 FREE( cmap
->c
.cmap2
.subHeaderKeys
);
316 FREE( cmap
->c
.cmap2
.subHeaders
);
317 FREE( cmap
->c
.cmap2
.glyphIdArray
);
321 FREE( cmap
->c
.cmap4
.segments
);
322 FREE( cmap
->c
.cmap4
.glyphIdArray
);
323 cmap
->c
.cmap4
.segCountX2
= 0;
327 FREE( cmap
->c
.cmap6
.glyphIdArray
);
328 cmap
->c
.cmap6
.entryCount
= 0;
332 /* invalid table format, do nothing */
336 cmap
->loaded
= FALSE
;
341 /*************************************************************************/
347 /* Converts the character code into a glyph index. Uses format 0. */
348 /* `charCode' must be in the range 0x00-0xFF (otherwise 0 is */
352 /* charCode :: The wanted character code. */
353 /* cmap0 :: A pointer to a cmap table in format 0. */
356 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
359 FT_UInt
code_to_index0( TT_CMapTable
* cmap
,
362 TT_CMap0
* cmap0
= &cmap
->c
.cmap0
;
365 return ( charCode
<= 0xFF ? cmap0
->glyphIdArray
[charCode
] : 0 );
369 /*************************************************************************/
375 /* Converts the character code into a glyph index. Uses format 2. */
378 /* charCode :: The wanted character code. */
379 /* cmap2 :: A pointer to a cmap table in format 2. */
382 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
385 FT_UInt
code_to_index2( TT_CMapTable
* cmap
,
388 FT_UInt result
, index1
, offset
;
391 TT_CMap2SubHeader
* sh2
;
395 cmap2
= &cmap
->c
.cmap2
;
397 char_lo
= (FT_UInt
)( charCode
& 0xFF );
398 char_hi
= charCode
>> 8;
402 /* an 8-bit character code -- we use the subHeader 0 in this case */
403 /* to test whether the character code is in the charmap */
404 if ( cmap2
->subHeaderKeys
[char_lo
] == 0 )
405 result
= cmap2
->glyphIdArray
[char_lo
];
409 /* a 16-bit character code */
410 index1
= cmap2
->subHeaderKeys
[char_hi
& 0xFF];
413 sh2
= cmap2
->subHeaders
+ index1
;
414 char_lo
-= sh2
->firstCode
;
416 if ( char_lo
< sh2
->entryCount
)
418 offset
= sh2
->idRangeOffset
/ 2 + char_lo
;
419 if ( offset
< cmap2
->numGlyphId
)
421 result
= cmap2
->glyphIdArray
[offset
];
423 result
= ( result
+ sh2
->idDelta
) & 0xFFFF;
433 /*************************************************************************/
439 /* Converts the character code into a glyph index. Uses format 4. */
442 /* charCode :: The wanted character code. */
443 /* cmap4 :: A pointer to a cmap table in format 4. */
446 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
449 FT_UInt
code_to_index4( TT_CMapTable
* cmap
,
452 FT_UInt result
, index1
, segCount
;
454 TT_CMap4Segment
*seg4
, *limit
;
457 cmap4
= &cmap
->c
.cmap4
;
459 segCount
= cmap4
->segCountX2
/ 2;
460 seg4
= cmap4
->segments
;
461 limit
= seg4
+ segCount
;
463 /* check against the last segment */
464 seg4
= cmap4
->last_segment
;
466 /* the following is equivalent to performing two tests, as in */
468 /* if ( charCode >= seg4->startCount && charCode <= seg4->endCount ) */
470 /* Yes, that's a bit strange, but it's faster, and the idea behind */
471 /* the cache is to significantly speed up charcode to glyph index */
474 if ( (FT_ULong
)(charCode
- seg4
->startCount
) <
475 (FT_ULong
)(seg4
->endCount
- seg4
->startCount
) )
478 for ( seg4
= cmap4
->segments
; seg4
< limit
; seg4
++ )
480 /* the ranges are sorted in increasing order. If we are out of */
481 /* the range here, the char code isn't in the charmap, so exit. */
483 if ( charCode
> seg4
->endCount
)
486 if ( charCode
>= seg4
->startCount
)
492 cmap4
->last_segment
= seg4
;
494 /* if the idRangeOffset is 0, we can compute the glyph index */
497 if ( seg4
->idRangeOffset
== 0 )
498 result
= ( charCode
+ seg4
->idDelta
) & 0xFFFF;
501 /* otherwise, we must use the glyphIdArray to do it */
502 index1
= seg4
->idRangeOffset
/ 2
503 + ( charCode
- seg4
->startCount
)
504 + ( seg4
- cmap4
->segments
)
507 if ( index1
< cmap4
->numGlyphId
&&
508 cmap4
->glyphIdArray
[index1
] != 0 )
509 result
= ( cmap4
->glyphIdArray
[index1
] + seg4
->idDelta
) & 0xFFFF;
516 /*************************************************************************/
522 /* Converts the character code into a glyph index. Uses format 6. */
525 /* charCode :: The wanted character code. */
526 /* cmap6 :: A pointer to a cmap table in format 6. */
529 /* Glyph index into the glyphs array. 0 if the glyph does not exist. */
532 FT_UInt
code_to_index6( TT_CMapTable
* cmap
,
539 cmap6
= &cmap
->c
.cmap6
;
541 charCode
-= cmap6
->firstCode
;
543 if ( charCode
< cmap6
->entryCount
)
544 result
= cmap6
->glyphIdArray
[charCode
];