]>
Commit | Line | Data |
---|---|---|
1 | /***************************************************************************/ | |
2 | /* */ | |
3 | /* ttcmap.c */ | |
4 | /* */ | |
5 | /* TrueType character mapping table (cmap) support (body). */ | |
6 | /* */ | |
7 | /* Copyright 1996-2000 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 | /***************************************************************************/ | |
17 | ||
18 | ||
19 | #include <freetype/internal/ftdebug.h> | |
20 | #include <freetype/internal/tterrors.h> | |
21 | ||
22 | ||
23 | #ifdef FT_FLAT_COMPILE | |
24 | ||
25 | #include "ttload.h" | |
26 | #include "ttcmap.h" | |
27 | ||
28 | #else | |
29 | ||
30 | #include <sfnt/ttload.h> | |
31 | #include <sfnt/ttcmap.h> | |
32 | ||
33 | #endif | |
34 | ||
35 | ||
36 | /*************************************************************************/ | |
37 | /* */ | |
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. */ | |
41 | /* */ | |
42 | #undef FT_COMPONENT | |
43 | #define FT_COMPONENT trace_ttcmap | |
44 | ||
45 | ||
46 | static FT_UInt code_to_index0( TT_CMapTable* charmap, | |
47 | FT_ULong char_code ); | |
48 | static FT_UInt code_to_index2( TT_CMapTable* charmap, | |
49 | FT_ULong char_code ); | |
50 | static FT_UInt code_to_index4( TT_CMapTable* charmap, | |
51 | FT_ULong char_code ); | |
52 | static FT_UInt code_to_index6( TT_CMapTable* charmap, | |
53 | FT_ULong char_code ); | |
54 | ||
55 | ||
56 | /*************************************************************************/ | |
57 | /* */ | |
58 | /* <Function> */ | |
59 | /* TT_CharMap_Load */ | |
60 | /* */ | |
61 | /* <Description> */ | |
62 | /* Loads a given TrueType character map into memory. */ | |
63 | /* */ | |
64 | /* <Input> */ | |
65 | /* face :: A handle to the parent face object. */ | |
66 | /* stream :: A handle to the current stream object. */ | |
67 | /* */ | |
68 | /* <InOut> */ | |
69 | /* table :: A pointer to a cmap object. */ | |
70 | /* */ | |
71 | /* <Return> */ | |
72 | /* FreeType error code. 0 means success. */ | |
73 | /* */ | |
74 | /* <Note> */ | |
75 | /* The function assumes that the stream is already in use (i.e., */ | |
76 | /* opened). In case of error, all partially allocated tables are */ | |
77 | /* released. */ | |
78 | /* */ | |
79 | LOCAL_FUNC | |
80 | FT_Error TT_CharMap_Load( TT_Face face, | |
81 | TT_CMapTable* cmap, | |
82 | FT_Stream stream ) | |
83 | { | |
84 | FT_Error error; | |
85 | FT_Memory memory; | |
86 | FT_UShort num_SH, num_Seg, i; | |
87 | ||
88 | FT_UShort u, l; | |
89 | ||
90 | TT_CMap0* cmap0; | |
91 | TT_CMap2* cmap2; | |
92 | TT_CMap4* cmap4; | |
93 | TT_CMap6* cmap6; | |
94 | ||
95 | TT_CMap2SubHeader* cmap2sub; | |
96 | TT_CMap4Segment* segments; | |
97 | ||
98 | ||
99 | if ( cmap->loaded ) | |
100 | return TT_Err_Ok; | |
101 | ||
102 | memory = stream->memory; | |
103 | ||
104 | if ( FILE_Seek( cmap->offset ) ) | |
105 | return error; | |
106 | ||
107 | switch ( cmap->format ) | |
108 | { | |
109 | case 0: | |
110 | cmap0 = &cmap->c.cmap0; | |
111 | ||
112 | if ( ALLOC( cmap0->glyphIdArray, 256L ) || | |
113 | FILE_Read( cmap0->glyphIdArray, 256L ) ) | |
114 | goto Fail; | |
115 | ||
116 | cmap->get_index = code_to_index0; | |
117 | break; | |
118 | ||
119 | case 2: | |
120 | num_SH = 0; | |
121 | cmap2 = &cmap->c.cmap2; | |
122 | ||
123 | /* allocate subheader keys */ | |
124 | ||
125 | if ( ALLOC_ARRAY( cmap2->subHeaderKeys, 256, FT_UShort ) || | |
126 | ACCESS_Frame( 512L ) ) | |
127 | goto Fail; | |
128 | ||
129 | for ( i = 0; i < 256; i++ ) | |
130 | { | |
131 | u = GET_UShort() / 8; | |
132 | cmap2->subHeaderKeys[i] = u; | |
133 | ||
134 | if ( num_SH < u ) | |
135 | num_SH = u; | |
136 | } | |
137 | ||
138 | FORGET_Frame(); | |
139 | ||
140 | /* load subheaders */ | |
141 | ||
142 | cmap2->numGlyphId = l = | |
143 | ( ( cmap->length - 2L * ( 256 + 3 ) - num_SH * 8L ) & 0xFFFF ) / 2; | |
144 | ||
145 | if ( ALLOC_ARRAY( cmap2->subHeaders, | |
146 | num_SH + 1, | |
147 | TT_CMap2SubHeader ) || | |
148 | ACCESS_Frame( ( num_SH + 1 ) * 8L ) ) | |
149 | goto Fail; | |
150 | ||
151 | cmap2sub = cmap2->subHeaders; | |
152 | ||
153 | for ( i = 0; i <= num_SH; i++ ) | |
154 | { | |
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; | |
160 | ||
161 | cmap2sub++; | |
162 | } | |
163 | ||
164 | FORGET_Frame(); | |
165 | ||
166 | /* load glyph IDs */ | |
167 | ||
168 | if ( ALLOC_ARRAY( cmap2->glyphIdArray, l, FT_UShort ) || | |
169 | ACCESS_Frame( l * 2L ) ) | |
170 | goto Fail; | |
171 | ||
172 | for ( i = 0; i < l; i++ ) | |
173 | cmap2->glyphIdArray[i] = GET_UShort(); | |
174 | ||
175 | FORGET_Frame(); | |
176 | ||
177 | cmap->get_index = code_to_index2; | |
178 | break; | |
179 | ||
180 | case 4: | |
181 | cmap4 = &cmap->c.cmap4; | |
182 | ||
183 | /* load header */ | |
184 | ||
185 | if ( ACCESS_Frame( 8L ) ) | |
186 | goto Fail; | |
187 | ||
188 | cmap4->segCountX2 = GET_UShort(); | |
189 | cmap4->searchRange = GET_UShort(); | |
190 | cmap4->entrySelector = GET_UShort(); | |
191 | cmap4->rangeShift = GET_UShort(); | |
192 | ||
193 | num_Seg = cmap4->segCountX2 / 2; | |
194 | ||
195 | FORGET_Frame(); | |
196 | ||
197 | /* load segments */ | |
198 | ||
199 | if ( ALLOC_ARRAY( cmap4->segments, | |
200 | num_Seg, | |
201 | TT_CMap4Segment ) || | |
202 | ACCESS_Frame( ( num_Seg * 4 + 1 ) * 2L ) ) | |
203 | goto Fail; | |
204 | ||
205 | segments = cmap4->segments; | |
206 | ||
207 | for ( i = 0; i < num_Seg; i++ ) | |
208 | segments[i].endCount = GET_UShort(); | |
209 | ||
210 | (void)GET_UShort(); | |
211 | ||
212 | for ( i = 0; i < num_Seg; i++ ) | |
213 | segments[i].startCount = GET_UShort(); | |
214 | ||
215 | for ( i = 0; i < num_Seg; i++ ) | |
216 | segments[i].idDelta = GET_Short(); | |
217 | ||
218 | for ( i = 0; i < num_Seg; i++ ) | |
219 | segments[i].idRangeOffset = GET_UShort(); | |
220 | ||
221 | FORGET_Frame(); | |
222 | ||
223 | cmap4->numGlyphId = l = | |
224 | ( ( cmap->length - ( 16L + 8L * num_Seg ) ) & 0xFFFF ) / 2; | |
225 | ||
226 | /* load IDs */ | |
227 | ||
228 | if ( ALLOC_ARRAY( cmap4->glyphIdArray, l, FT_UShort ) || | |
229 | ACCESS_Frame( l * 2L ) ) | |
230 | goto Fail; | |
231 | ||
232 | for ( i = 0; i < l; i++ ) | |
233 | cmap4->glyphIdArray[i] = GET_UShort(); | |
234 | ||
235 | FORGET_Frame(); | |
236 | ||
237 | cmap->get_index = code_to_index4; | |
238 | ||
239 | cmap4->last_segment = cmap4->segments; | |
240 | break; | |
241 | ||
242 | case 6: | |
243 | cmap6 = &cmap->c.cmap6; | |
244 | ||
245 | if ( ACCESS_Frame( 4L ) ) | |
246 | goto Fail; | |
247 | ||
248 | cmap6->firstCode = GET_UShort(); | |
249 | cmap6->entryCount = GET_UShort(); | |
250 | ||
251 | FORGET_Frame(); | |
252 | ||
253 | l = cmap6->entryCount; | |
254 | ||
255 | if ( ALLOC_ARRAY( cmap6->glyphIdArray, | |
256 | cmap6->entryCount, | |
257 | FT_Short ) || | |
258 | ACCESS_Frame( l * 2L ) ) | |
259 | goto Fail; | |
260 | ||
261 | for ( i = 0; i < l; i++ ) | |
262 | cmap6->glyphIdArray[i] = GET_UShort(); | |
263 | ||
264 | FORGET_Frame(); | |
265 | cmap->get_index = code_to_index6; | |
266 | break; | |
267 | ||
268 | default: /* corrupt character mapping table */ | |
269 | return TT_Err_Invalid_CharMap_Format; | |
270 | ||
271 | } | |
272 | ||
273 | return TT_Err_Ok; | |
274 | ||
275 | Fail: | |
276 | TT_CharMap_Free( face, cmap ); | |
277 | return error; | |
278 | } | |
279 | ||
280 | ||
281 | /*************************************************************************/ | |
282 | /* */ | |
283 | /* <Function> */ | |
284 | /* TT_CharMap_Free */ | |
285 | /* */ | |
286 | /* <Description> */ | |
287 | /* Destroys a character mapping table. */ | |
288 | /* */ | |
289 | /* <Input> */ | |
290 | /* face :: A handle to the parent face object. */ | |
291 | /* cmap :: A handle to a cmap object. */ | |
292 | /* */ | |
293 | /* <Return> */ | |
294 | /* FreeType error code. 0 means success. */ | |
295 | /* */ | |
296 | LOCAL_FUNC | |
297 | FT_Error TT_CharMap_Free( TT_Face face, | |
298 | TT_CMapTable* cmap ) | |
299 | { | |
300 | FT_Memory memory; | |
301 | ||
302 | ||
303 | if ( !cmap ) | |
304 | return TT_Err_Ok; | |
305 | ||
306 | memory = face->root.driver->root.memory; | |
307 | ||
308 | switch ( cmap->format ) | |
309 | { | |
310 | case 0: | |
311 | FREE( cmap->c.cmap0.glyphIdArray ); | |
312 | break; | |
313 | ||
314 | case 2: | |
315 | FREE( cmap->c.cmap2.subHeaderKeys ); | |
316 | FREE( cmap->c.cmap2.subHeaders ); | |
317 | FREE( cmap->c.cmap2.glyphIdArray ); | |
318 | break; | |
319 | ||
320 | case 4: | |
321 | FREE( cmap->c.cmap4.segments ); | |
322 | FREE( cmap->c.cmap4.glyphIdArray ); | |
323 | cmap->c.cmap4.segCountX2 = 0; | |
324 | break; | |
325 | ||
326 | case 6: | |
327 | FREE( cmap->c.cmap6.glyphIdArray ); | |
328 | cmap->c.cmap6.entryCount = 0; | |
329 | break; | |
330 | ||
331 | default: | |
332 | /* invalid table format, do nothing */ | |
333 | ; | |
334 | } | |
335 | ||
336 | cmap->loaded = FALSE; | |
337 | return TT_Err_Ok; | |
338 | } | |
339 | ||
340 | ||
341 | /*************************************************************************/ | |
342 | /* */ | |
343 | /* <Function> */ | |
344 | /* code_to_index0 */ | |
345 | /* */ | |
346 | /* <Description> */ | |
347 | /* Converts the character code into a glyph index. Uses format 0. */ | |
348 | /* `charCode' must be in the range 0x00-0xFF (otherwise 0 is */ | |
349 | /* returned). */ | |
350 | /* */ | |
351 | /* <Input> */ | |
352 | /* charCode :: The wanted character code. */ | |
353 | /* cmap0 :: A pointer to a cmap table in format 0. */ | |
354 | /* */ | |
355 | /* <Return> */ | |
356 | /* Glyph index into the glyphs array. 0 if the glyph does not exist. */ | |
357 | /* */ | |
358 | static | |
359 | FT_UInt code_to_index0( TT_CMapTable* cmap, | |
360 | FT_ULong charCode ) | |
361 | { | |
362 | TT_CMap0* cmap0 = &cmap->c.cmap0; | |
363 | ||
364 | ||
365 | return ( charCode <= 0xFF ? cmap0->glyphIdArray[charCode] : 0 ); | |
366 | } | |
367 | ||
368 | ||
369 | /*************************************************************************/ | |
370 | /* */ | |
371 | /* <Function> */ | |
372 | /* code_to_index2 */ | |
373 | /* */ | |
374 | /* <Description> */ | |
375 | /* Converts the character code into a glyph index. Uses format 2. */ | |
376 | /* */ | |
377 | /* <Input> */ | |
378 | /* charCode :: The wanted character code. */ | |
379 | /* cmap2 :: A pointer to a cmap table in format 2. */ | |
380 | /* */ | |
381 | /* <Return> */ | |
382 | /* Glyph index into the glyphs array. 0 if the glyph does not exist. */ | |
383 | /* */ | |
384 | static | |
385 | FT_UInt code_to_index2( TT_CMapTable* cmap, | |
386 | FT_ULong charCode ) | |
387 | { | |
388 | FT_UInt result, index1, offset; | |
389 | FT_UInt char_lo; | |
390 | FT_ULong char_hi; | |
391 | TT_CMap2SubHeader* sh2; | |
392 | TT_CMap2* cmap2; | |
393 | ||
394 | ||
395 | cmap2 = &cmap->c.cmap2; | |
396 | result = 0; | |
397 | char_lo = (FT_UInt)( charCode & 0xFF ); | |
398 | char_hi = charCode >> 8; | |
399 | ||
400 | if ( char_hi == 0 ) | |
401 | { | |
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]; | |
406 | } | |
407 | else | |
408 | { | |
409 | /* a 16-bit character code */ | |
410 | index1 = cmap2->subHeaderKeys[char_hi & 0xFF]; | |
411 | if ( index1 ) | |
412 | { | |
413 | sh2 = cmap2->subHeaders + index1; | |
414 | char_lo -= sh2->firstCode; | |
415 | ||
416 | if ( char_lo < sh2->entryCount ) | |
417 | { | |
418 | offset = sh2->idRangeOffset / 2 + char_lo; | |
419 | if ( offset < cmap2->numGlyphId ) | |
420 | { | |
421 | result = cmap2->glyphIdArray[offset]; | |
422 | if ( result ) | |
423 | result = ( result + sh2->idDelta ) & 0xFFFF; | |
424 | } | |
425 | } | |
426 | } | |
427 | } | |
428 | ||
429 | return result; | |
430 | } | |
431 | ||
432 | ||
433 | /*************************************************************************/ | |
434 | /* */ | |
435 | /* <Function> */ | |
436 | /* code_to_index4 */ | |
437 | /* */ | |
438 | /* <Description> */ | |
439 | /* Converts the character code into a glyph index. Uses format 4. */ | |
440 | /* */ | |
441 | /* <Input> */ | |
442 | /* charCode :: The wanted character code. */ | |
443 | /* cmap4 :: A pointer to a cmap table in format 4. */ | |
444 | /* */ | |
445 | /* <Return> */ | |
446 | /* Glyph index into the glyphs array. 0 if the glyph does not exist. */ | |
447 | /* */ | |
448 | static | |
449 | FT_UInt code_to_index4( TT_CMapTable* cmap, | |
450 | FT_ULong charCode ) | |
451 | { | |
452 | FT_UInt result, index1, segCount; | |
453 | TT_CMap4* cmap4; | |
454 | TT_CMap4Segment *seg4, *limit; | |
455 | ||
456 | ||
457 | cmap4 = &cmap->c.cmap4; | |
458 | result = 0; | |
459 | segCount = cmap4->segCountX2 / 2; | |
460 | seg4 = cmap4->segments; | |
461 | limit = seg4 + segCount; | |
462 | ||
463 | /* check against the last segment */ | |
464 | seg4 = cmap4->last_segment; | |
465 | ||
466 | /* the following is equivalent to performing two tests, as in */ | |
467 | /* */ | |
468 | /* if ( charCode >= seg4->startCount && charCode <= seg4->endCount ) */ | |
469 | /* */ | |
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 */ | |
472 | /* conversion. */ | |
473 | ||
474 | if ( (FT_ULong)(charCode - seg4->startCount) < | |
475 | (FT_ULong)(seg4->endCount - seg4->startCount) ) | |
476 | goto Found; | |
477 | ||
478 | for ( seg4 = cmap4->segments; seg4 < limit; seg4++ ) | |
479 | { | |
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. */ | |
482 | ||
483 | if ( charCode > seg4->endCount ) | |
484 | continue; | |
485 | ||
486 | if ( charCode >= seg4->startCount ) | |
487 | goto Found; | |
488 | } | |
489 | return 0; | |
490 | ||
491 | Found: | |
492 | cmap4->last_segment = seg4; | |
493 | ||
494 | /* if the idRangeOffset is 0, we can compute the glyph index */ | |
495 | /* directly */ | |
496 | ||
497 | if ( seg4->idRangeOffset == 0 ) | |
498 | result = ( charCode + seg4->idDelta ) & 0xFFFF; | |
499 | else | |
500 | { | |
501 | /* otherwise, we must use the glyphIdArray to do it */ | |
502 | index1 = seg4->idRangeOffset / 2 | |
503 | + ( charCode - seg4->startCount ) | |
504 | + ( seg4 - cmap4->segments ) | |
505 | - segCount; | |
506 | ||
507 | if ( index1 < cmap4->numGlyphId && | |
508 | cmap4->glyphIdArray[index1] != 0 ) | |
509 | result = ( cmap4->glyphIdArray[index1] + seg4->idDelta ) & 0xFFFF; | |
510 | } | |
511 | ||
512 | return result; | |
513 | } | |
514 | ||
515 | ||
516 | /*************************************************************************/ | |
517 | /* */ | |
518 | /* <Function> */ | |
519 | /* code_to_index6 */ | |
520 | /* */ | |
521 | /* <Description> */ | |
522 | /* Converts the character code into a glyph index. Uses format 6. */ | |
523 | /* */ | |
524 | /* <Input> */ | |
525 | /* charCode :: The wanted character code. */ | |
526 | /* cmap6 :: A pointer to a cmap table in format 6. */ | |
527 | /* */ | |
528 | /* <Return> */ | |
529 | /* Glyph index into the glyphs array. 0 if the glyph does not exist. */ | |
530 | /* */ | |
531 | static | |
532 | FT_UInt code_to_index6( TT_CMapTable* cmap, | |
533 | FT_ULong charCode ) | |
534 | { | |
535 | TT_CMap6* cmap6; | |
536 | FT_UInt result = 0; | |
537 | ||
538 | ||
539 | cmap6 = &cmap->c.cmap6; | |
540 | result = 0; | |
541 | charCode -= cmap6->firstCode; | |
542 | ||
543 | if ( charCode < cmap6->entryCount ) | |
544 | result = cmap6->glyphIdArray[charCode]; | |
545 | ||
546 | return result; | |
547 | } | |
548 | ||
549 | ||
550 | /* END */ |